diff options
author | Gary Wong <gtw@gnu.org> | 2009-09-01 20:51:40 -0600 |
---|---|---|
committer | Gary Wong <gtw@gnu.org> | 2009-09-01 20:51:40 -0600 |
commit | db6024a4eb1f55605e344d9cb640424038a239be (patch) | |
tree | e453835cc097c67441d2de2dba60dd0c87b13a8d /menu.c | |
parent | 86c8dfa5b8017f7a940ed2e7333ee27a4af199a8 (diff) |
Add simple menu handling.
Diffstat (limited to 'menu.c')
-rw-r--r-- | menu.c | 325 |
1 files changed, 325 insertions, 0 deletions
@@ -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 */ +}; |