summaryrefslogtreecommitdiff
path: root/menu.c
diff options
context:
space:
mode:
authorGary Wong <gtw@gnu.org>2009-09-01 20:51:40 -0600
committerGary Wong <gtw@gnu.org>2009-09-01 20:51:40 -0600
commitdb6024a4eb1f55605e344d9cb640424038a239be (patch)
treee453835cc097c67441d2de2dba60dd0c87b13a8d /menu.c
parent86c8dfa5b8017f7a940ed2e7333ee27a4af199a8 (diff)
Add simple menu handling.
Diffstat (limited to 'menu.c')
-rw-r--r--menu.c325
1 files changed, 325 insertions, 0 deletions
diff --git a/menu.c b/menu.c
new file mode 100644
index 0000000..3497103
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,325 @@
+/*
+ * menu.c
+ *
+ * Part of gwm, the Gratuitous Window Manager,
+ * by Gary Wong, <gtw@gnu.org>.
+ *
+ * Copyright (C) 2009 Gary Wong
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xcb/xcb.h>
+
+#include "gwm.h"
+
+#include "menu.h"
+
+#include "actions.h"
+#include "frame.h"
+#include "keyboard.h"
+#include "managed.h"
+#include "root.h"
+#include "window-table.h"
+
+static void destroy_menu( struct gwm_window *menu ) {
+
+ int i;
+
+ for( i = 0; i < menu->u.menu.num_items; i++ ) {
+ free( menu->u.menu.items[ i ]->u.menuitem.label );
+ forget_window( menu->u.menu.items[ i ] );
+ }
+
+ free( menu->u.menu.items );
+
+ xcb_destroy_window( c, menu->w );
+
+ forget_window( menu );
+}
+
+static void menu_button_release( struct gwm_window *window,
+ xcb_button_release_event_t *ev ) {
+
+ struct gwm_window *window_param;
+ void ( *action )( struct gwm_window *window, xcb_generic_event_t *ev,
+ union callback_param cp );
+ union callback_param cp;
+
+ if( final_release( ev ) ) {
+ if( window->u.menu.active_item >= 0 ) {
+ window_param = window->u.menu.window_param;
+ action = window->u.menu.items[ window->u.menu.active_item ]->
+ u.menuitem.action;
+ cp = window->u.menu.items[ window->u.menu.active_item ]->
+ u.menuitem.cp;
+ } else
+ action = NULL;
+
+ destroy_menu( window );
+
+ if( action )
+ action( window_param, (xcb_generic_event_t *) ev, cp );
+ }
+}
+
+static void menu_enter_notify( struct gwm_window *window,
+ xcb_enter_notify_event_t *ev ) {
+
+ struct gwm_window *child;
+
+ if( !pointer_demux )
+ return;
+
+ if( ( child = lookup_window( ev->event ) ) &&
+ child->type == WINDOW_MENUITEM ) {
+ uint32_t n;
+ int i, width, height;
+
+ for( i = 0; i < window->u.menu.num_items; i++ )
+ if( window->u.menu.items[ i ] == child )
+ break;
+
+ if( i == window->u.menu.num_items || i == window->u.menu.active_item )
+ return;
+
+ if( child->u.menuitem.label )
+ window_size( child, &width, &height );
+ else {
+ window_size( window->u.menu.items[ window->u.menu.active_item ],
+ &width, &height );
+ i = -1;
+ }
+
+ if( window->u.menu.active_item >= 0 ) {
+ n = gwm_screens[ window->screen ].pixels[ COL_MENU_INACTIVE_BACK ];
+ xcb_change_window_attributes( c, window->u.menu.items[
+ window->u.menu.active_item ]->w,
+ XCB_CW_BACK_PIXEL, &n );
+ queue_window_update( window->u.menu.items[
+ window->u.menu.active_item ], 0, 0,
+ window->u.menu.width, height, FALSE );
+ }
+
+ if( i >= 0 ) {
+ n = gwm_screens[ window->screen ].pixels[ COL_MENU_ACTIVE_BACK ];
+ xcb_change_window_attributes( c, child->w, XCB_CW_BACK_PIXEL, &n );
+ queue_window_update( child, 0, 0,
+ window->u.menu.width, height, FALSE );
+ }
+
+ window->u.menu.active_item = i;
+ }
+}
+
+static void menu_leave_notify( struct gwm_window *window,
+ xcb_leave_notify_event_t *ev ) {
+
+ uint32_t n;
+ int width, height;
+
+ if( !pointer_demux || ev->detail == XCB_NOTIFY_DETAIL_INFERIOR )
+ return;
+
+ if( window->u.menu.active_item >= 0 ) {
+ window_size( window->u.menu.items[ 0 ], &width, &height );
+
+ n = gwm_screens[ window->screen ].pixels[ COL_MENU_INACTIVE_BACK ];
+ xcb_change_window_attributes( c, window->u.menu.items[
+ window->u.menu.active_item ]->w,
+ XCB_CW_BACK_PIXEL, &n );
+ queue_window_update( window->u.menu.items[
+ window->u.menu.active_item ], 0, 0,
+ window->u.menu.width, height, FALSE );
+
+ window->u.menu.active_item = -1;
+ }
+}
+
+extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev,
+ int num_items, const struct menuitem *items ) {
+
+ struct gwm_window *menu, *item;
+ int i, y, height, *widths, *heights, ev_x, ev_y;
+ xcb_timestamp_t timestamp;
+ uint32_t values[ 4 ];
+ xcb_button_press_event_t *bp = (xcb_button_press_event_t *) ev;
+ int screen = window->screen;
+
+ if( num_items < 1 )
+ return;
+
+ switch( ev->response_type ) {
+ case XCB_BUTTON_PRESS:
+ ev_x = bp->root_x;
+ ev_y = bp->root_y;
+ timestamp = bp->time;
+ break;
+
+ default:
+ assert( FALSE );
+ }
+
+ menu = add_window( xcb_generate_id( c ) );
+ menu->screen = screen;
+ menu->type = WINDOW_MENU;
+ menu->u.menu.window_param = window;
+ menu->u.menu.num_items = num_items;
+ menu->u.menu.active_item = -1;
+ menu->u.menu.items = xmalloc( num_items * sizeof *menu->u.menu.items );
+
+ widths = alloca( num_items * sizeof *widths );
+ heights = alloca( num_items * sizeof *widths );
+
+ menu->u.menu.width = height = 0;
+ for( i = 0; i < num_items; i++ ) {
+ menu->u.menu.items[ i ] = item = add_window( xcb_generate_id( c ) );
+ item->screen = screen;
+ item->type = WINDOW_MENUITEM;
+ item->u.menuitem.menu = menu;
+ item->u.menuitem.label = strdup( items[ i ].label );
+ item->u.menuitem.action = items[ i ].action;
+ item->u.menuitem.cp = items[ i ].cp;
+ window_size( item, widths + i, heights + i );
+
+ if( widths[ i ] > menu->u.menu.width )
+ menu->u.menu.width = widths[ i ];
+
+ height += heights[ i ];
+ }
+
+ if( ev_x + menu->u.menu.width > screens[ screen ]->width_in_pixels )
+ ev_x = screens[ screen ]->width_in_pixels - menu->u.menu.width;
+ if( ev_x < 0 )
+ ev_x = 0;
+
+ if( ev_y + height > screens[ screen ]->height_in_pixels )
+ ev_y = screens[ screen ]->height_in_pixels - height;
+ if( ev_y < 0 )
+ ev_y = 0;
+
+ values[ 0 ] = gwm_screens[ screen ].pixels[ COL_BORDER ]; /* border pixel */
+ values[ 1 ] = TRUE; /* override redirect */
+ values[ 2 ] = TRUE; /* save under */
+ values[ 3 ] = XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_ENTER_WINDOW |
+ XCB_EVENT_MASK_LEAVE_WINDOW;
+ xcb_create_window( c, XCB_COPY_FROM_PARENT, menu->w,
+ screens[ screen ]->root, ev_x, ev_y, menu->u.menu.width,
+ height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ XCB_COPY_FROM_PARENT, XCB_CW_BORDER_PIXEL |
+ XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER |
+ XCB_CW_EVENT_MASK, values );
+
+ values[ 0 ] = gwm_screens[ screen ].pixels[
+ COL_MENU_INACTIVE_BACK ]; /* background pixel */
+ values[ 1 ] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_EXPOSURE;
+ y = 0;
+ for( i = 0; i < num_items; i++ ) {
+ xcb_create_window( c, XCB_COPY_FROM_PARENT, menu->u.menu.items[ i ]->w,
+ menu->w, 0, y, menu->u.menu.width, heights[ i ], 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
+ XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, values );
+
+ y += heights[ i ];
+ }
+
+ xcb_map_subwindows( c, menu->w );
+ xcb_map_window( c, menu->w );
+
+ pointer_demux = menu->w;
+}
+
+event_handler menu_handlers[] = {
+ NULL, /* Error */
+ NULL, /* Reply */
+ NULL, /* KeyPress */
+ NULL, /* KeyRelease */
+ NULL, /* ButtonPress */
+ (event_handler) menu_button_release,
+ NULL, /* MotionNotify */
+ (event_handler) menu_enter_notify,
+ (event_handler) menu_leave_notify,
+ NULL, /* FocusIn */
+ NULL, /* FocusOut */
+ NULL, /* KeymapNotify */
+ NULL, /* Expose */
+ NULL, /* GraphicsExpose */
+ NULL, /* NoExposure */
+ NULL, /* VisibilityNotify */
+ NULL, /* CreateNotify */
+ NULL, /* DestroyNotify */
+ NULL, /* UnmapNotify */
+ NULL, /* MapNotify */
+ NULL, /* MapRequest */
+ NULL, /* ReparentNotify */
+ NULL, /* ConfigureNotify */
+ NULL, /* ConfigureRequest */
+ NULL, /* GravityNotify */
+ NULL, /* ResizeRequest */
+ NULL, /* CirculateNotify */
+ NULL, /* CirculateRequest */
+ NULL, /* PropertyNotify */
+ NULL, /* SelectionClear */
+ NULL, /* SelectionRequest */
+ NULL, /* SelectionNotify */
+ NULL, /* ColormapNotify */
+ NULL, /* ClientMessage */
+ NULL, /* MappingNotify */
+ NULL, /* (synthetic) */
+ NULL /* ShapeNotify */
+}, menuitem_handlers[] = {
+ NULL, /* Error */
+ NULL, /* Reply */
+ NULL, /* KeyPress */
+ NULL, /* KeyRelease */
+ NULL, /* ButtonPress */
+ NULL, /* ButtonRelease */
+ NULL, /* MotionNotify */
+ NULL, /* EnterNotify */
+ NULL, /* LeaveNotify */
+ NULL, /* FocusIn */
+ NULL, /* FocusOut */
+ NULL, /* KeymapNotify */
+ (event_handler) generic_expose,
+ NULL, /* GraphicsExpose */
+ NULL, /* NoExposure */
+ NULL, /* VisibilityNotify */
+ NULL, /* CreateNotify */
+ NULL, /* DestroyNotify */
+ NULL, /* UnmapNotify */
+ NULL, /* MapNotify */
+ NULL, /* MapRequest */
+ NULL, /* ReparentNotify */
+ NULL, /* ConfigureNotify */
+ NULL, /* ConfigureRequest */
+ NULL, /* GravityNotify */
+ NULL, /* ResizeRequest */
+ NULL, /* CirculateNotify */
+ NULL, /* CirculateRequest */
+ NULL, /* PropertyNotify */
+ NULL, /* SelectionClear */
+ NULL, /* SelectionRequest */
+ NULL, /* SelectionNotify */
+ NULL, /* ColormapNotify */
+ NULL, /* ClientMessage */
+ NULL, /* MappingNotify */
+ NULL, /* (synthetic) */
+ NULL /* ShapeNotify */
+};