Logo Search packages:      
Sourcecode: afterstep version File versions

menus.c

/*
 * Copyright (c) 2002 Sasha Vasko
 * module has been entirely rewritten to use ASCanvas from original menus.c
 * that was implemented by :
 *      Copyright (C) 1993 Rob Nation
 *      Copyright (C) 1995 Bo Yang
 *      Copyright (C) 1996 Frank Fejes
 *      Copyright (C) 1996 Alfredo Kojima
 *      Copyright (C) 1998 Guylhem Aznar
 *      Copyright (C) 1998, 1999 Ethan Fischer
 *
 * 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
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/***********************************************************************
 * afterstep menu code
 ***********************************************************************/

#define LOCAL_DEBUG

#include "../../configure.h"

#include "asinternals.h"
#include "menus.h"


#include <X11/keysym.h>

#define START_LONG_DRAW_OPERATION   grab_server()
#define STOP_LONG_DRAW_OPERATION    ungrab_server()


static ASMenu *ASTopmostMenu = NULL;

ASHints *make_menu_hints( ASMenu *menu );
/*************************************************************************/
/* low level ASMenu functionality :                                      */
/*************************************************************************/
static Window
make_menu_window( Window parent )
{
    Window w;
    XSetWindowAttributes attr ;
    if( parent == None )
        parent = Scr.Root ;

      attr.cursor = Scr.Feel.cursors[ASCUR_Menu] ;
    attr.event_mask = AS_MENU_EVENT_MASK ;
    w = create_visual_window( Scr.asv, parent, -10, -10, 1, 1, 0, InputOutput, CWEventMask|CWCursor, &attr );

    if( w && parent != Scr.Root )
        XMapRaised( dpy, w );
    return w;
}


ASMenu *
create_asmenu( const char *name)
{
    ASMenu *menu = safecalloc( 1, sizeof(ASMenu) );
    Window w ;
    menu->magic = MAGIC_ASMENU ;
    w = make_menu_window( None );
    menu->main_canvas = create_ascanvas( w );
    menu->name = mystrdup(name);
      menu->scroll_up_bar = create_astbar();
      menu->scroll_down_bar = create_astbar();
    return menu;
}

static void
free_asmenu_item( ASMenuItem *item )
{
    if( item->bar )
        destroy_astbar(&(item->bar) );
    item->submenu = NULL ;
}

void destroy_asmenu(ASMenu **pmenu);

static inline void
close_asmenu_submenu( ASMenu *menu)
{
LOCAL_DEBUG_CALLER_OUT( "top(%p)->supermenu(%p)->menu(%p)->submenu(%p)", ASTopmostMenu, menu->supermenu, menu, menu->submenu );
    if( menu->submenu )
    {
            if( menu->submenu->supermenu == menu )
                  menu->submenu->supermenu = NULL ;
        if( menu->submenu->owner )
        {
            /* cannot use Destroy directly - must go through the normal channel: */
            unmap_canvas_window( menu->submenu->main_canvas );
            ASWIN_CLEAR_FLAGS(menu->submenu->owner, AS_Visible);
            ASWIN_SET_FLAGS(menu->submenu->owner, AS_UnMapPending);
                  menu->submenu = NULL ;
        }else
            destroy_asmenu( &(menu->submenu));
    }
}

void
destroy_asmenu(ASMenu **pmenu)
{
    if( pmenu )
    {
        ASMenu *menu = *pmenu;
        if( menu && menu->magic == MAGIC_ASMENU )
        {
            Window w = menu->main_canvas->w ;
LOCAL_DEBUG_CALLER_OUT( "top(%p)->supermenu(%p)->menu(%p)->submenu(%p)", ASTopmostMenu, menu->supermenu, menu, menu->submenu );

            if( menu->supermenu && menu->supermenu->submenu == menu )
                menu->supermenu->submenu = NULL ;
            else if( ASTopmostMenu == menu )
                ASTopmostMenu = NULL ;

            close_asmenu_submenu( menu);

            if( menu->main_canvas )
                destroy_ascanvas( &(menu->main_canvas) );
            if( menu->owner )
            {
                ASWIN_SET_FLAGS(menu->owner, AS_Dead );
                ASWIN_CLEAR_FLAGS(menu->owner, AS_Visible);
                ASWIN_SET_FLAGS(menu->owner, AS_UnMapPending);
            }
            destroy_registered_window( w );

            if( menu->items )
            {
                register int i = menu->items_num;
                while ( --i >= 0 )
                    free_asmenu_item( &(menu->items[i]));
                free( menu->items );
                menu->items = NULL ;
            }

                  if( menu->scroll_up_bar )
                        destroy_astbar(&(menu->scroll_up_bar ));
                  if( menu->scroll_down_bar )
                        destroy_astbar( &(menu->scroll_down_bar));

            if( menu->name )
                free( menu->name );
            if( menu->title )
                free( menu->title );
            menu->magic = 0 ;
            free( menu );
            *pmenu = NULL ;
        }
    }
}

void
close_asmenu( ASMenu **pmenu)
{
    if( pmenu )
    {
        ASMenu *menu = *pmenu ;
        if( menu )
        {
LOCAL_DEBUG_CALLER_OUT( "top(%p)->supermenu(%p)->menu(%p)->submenu(%p)", ASTopmostMenu, menu->supermenu, menu, menu->submenu );
                if( menu->submenu )
                  {
                        if( menu->submenu->supermenu == menu )
                              menu->submenu->supermenu = NULL ;
                        close_asmenu( &(menu->submenu) );
                  }
            if( menu->owner )
            {
                /* cannot use Destroy directly - must go through the normal channel: */
                unmap_canvas_window( menu->main_canvas );
                ASWIN_CLEAR_FLAGS(menu->owner, AS_Visible);
                ASWIN_SET_FLAGS(menu->owner, AS_UnMapPending);
            }else
                destroy_asmenu( &(menu));
            *pmenu = NULL ;
        }
    }
}

static void
set_asmenu_item_data( ASMenuItem *item, MenuDataItem *mdi )
{
    ASImage *icon_im = NULL ;

    item->source = mdi ;

    if( item->bar == NULL )
        item->bar = create_astbar();
      else
            delete_astbar_tile( item->bar, -1 );  /* delete all tiles */


    if( item->icon )
    {
        safe_asimage_destroy( item->icon );
        item->icon = NULL ;
    }
      /* we can only use images that are reference counted */
    if( mdi->minipixmap_image && mdi->minipixmap_image->imageman != NULL )
        icon_im = mdi->minipixmap_image ;
    else if( mdi->minipixmap )
        icon_im = GetASImageFromFile( mdi->minipixmap );
    if( icon_im )
      {
            item->icon = check_scale_menu_pmap( icon_im );
            if( item->icon != icon_im && icon_im != mdi->minipixmap_image )
                  safe_asimage_destroy( icon_im );
            if( item->icon == mdi->minipixmap_image )
            item->icon = dup_asimage( item->icon );
    }
LOCAL_DEBUG_OUT( "item(\"%s\")->minipixmap(\"%s\")->icon(%p)", mdi->item?mdi->item:"NULL", mdi->minipixmap?mdi->minipixmap:"NULL", item->icon );

    /* reserve space for minipixmap */
#define MI_LEFT_SPACER_IDX  0
    add_astbar_spacer( item->bar, 0, 0, 0, NO_ALIGN, 1, 1 );              /*0*/
#define MI_LEFT_ARROW_IDX   1
    add_astbar_spacer( item->bar, 1, 0, 0, NO_ALIGN, 1, 1 );              /*1*/
#define MI_LEFT_ICON_IDX    2
    add_astbar_spacer( item->bar, 2, 0, 0, NO_ALIGN, 1, 1 );              /*2*/
    /* reserve space for popup icon :*/
#define MI_POPUP_IDX        3
    add_astbar_spacer( item->bar, 7, 0, 0, NO_ALIGN, 1, 1 );              /*3*/

    /* optional menu items : */
    /* add label */
    if( mdi->item )
        add_astbar_label( item->bar, 3, 0, 0, ALIGN_LEFT|ALIGN_VCENTER, 0, 0, mdi->item,
            mdi->fdata->name_encoding );
    /* add hotkey */
    if( mdi->item2 )
        add_astbar_label( item->bar, 4, 0, 0, ALIGN_RIGHT|ALIGN_VCENTER, 0, 0, mdi->item2, mdi->fdata->name_encoding );

    item->flags = 0 ;
    if( mdi->fdata->func == F_POPUP )
    {
        if( (item->submenu = FindPopup (mdi->fdata->text, True)) == NULL )
            set_flags( item->flags, AS_MenuItemDisabled );
    }else if( get_flags( mdi->flags, MD_Disabled ) || mdi->fdata->func == F_NOP )
        set_flags( item->flags, AS_MenuItemDisabled );

    item->fdata = mdi->fdata ;
}

static Bool
set_asmenu_item_look( ASMenuItem *item, MyLook *look, unsigned int icon_space, unsigned int arrow_space )
{
    ASFlagType hilite = NO_HILITE, fhilite = NO_HILITE ;
    int subitem_offset = 1 ;
LOCAL_DEBUG_OUT( "item.bar(%p)->look(%p)", item->bar, look );

    if( item->bar == NULL )
        return False;

    item->bar->h_spacing = DEFAULT_MENU_SPACING ;
    item->bar->h_border = DEFAULT_MENU_ITEM_HBORDER ;
    item->bar->v_border = DEFAULT_MENU_ITEM_VBORDER ;

    delete_astbar_tile( item->bar, MI_LEFT_SPACER_IDX );
    if(get_flags( item->flags, AS_MenuItemSubitem ))
    {
        subitem_offset = icon_space+2+item->bar->h_spacing+item->bar->h_spacing;
        add_astbar_spacer( item->bar, 0, 0, 0, NO_ALIGN, subitem_offset, 1 );
    }
    delete_astbar_tile( item->bar, MI_LEFT_ARROW_IDX );
#if 1
    if(get_flags( item->flags, AS_MenuItemSubitem ))
    {
        if( look->MenuArrow )
          add_astbar_icon( item->bar, 1, 0, 0, ALIGN_VCENTER, look->MenuArrow->image );
        else
            add_astbar_spacer( item->bar, 1, 0, 0, NO_ALIGN, arrow_space, 1 );
    }
#endif
/*        add_astbar_spacer( item->bar, 1, 0, 0, NO_ALIGN, 1, 1 ); */

    if( get_flags( look->flags, MenuMiniPixmaps ) && icon_space > 0 )
    {
        /*set_astbar_tile_size( item->bar, MI_LEFT_SPACER_IDX, icon_space, 1 ); */
        delete_astbar_tile( item->bar, MI_LEFT_ICON_IDX );
        /* now readd it as minipixmap :*/
        if( item->icon )
            add_astbar_icon( item->bar, 2, 0, 0, ALIGN_VCENTER, item->icon );
        else
            add_astbar_spacer( item->bar, 2, 0, 0, NO_ALIGN, icon_space, 1 );
    }

    /* delete tile from Popup arrow cell : */
    delete_astbar_tile( item->bar, MI_POPUP_IDX );
    /* now readd it as proper type : */
    if( look->MenuArrow && item->fdata->func == F_POPUP )
        add_astbar_icon( item->bar, 7, 0, 0, ALIGN_VCENTER, look->MenuArrow->image );
    else
        add_astbar_spacer( item->bar, 7, 0, 0, NO_ALIGN, arrow_space, 1 );


    if( get_flags( item->flags, AS_MenuItemDisabled ) )
    {
        set_astbar_style_ptr( item->bar, BAR_STATE_UNFOCUSED, look->MSMenu[MENU_BACK_STIPPLE]);
        set_astbar_style_ptr( item->bar, BAR_STATE_FOCUSED, look->MSMenu[MENU_BACK_STIPPLE] );
    }else
    {
        set_astbar_style_ptr( item->bar, BAR_STATE_UNFOCUSED,
                              look->MSMenu[get_flags( item->flags, AS_MenuItemSubitem )?MENU_BACK_SUBITEM:
                                                                                        MENU_BACK_ITEM] );
        set_astbar_style_ptr( item->bar, BAR_STATE_FOCUSED, look->MSMenu[MENU_BACK_HILITE] );
    }
    if( look->DrawMenuBorders == DRAW_MENU_BORDERS_ITEM )
        fhilite = hilite = DEFAULT_MENU_HILITE;
    else if ( Scr.Look.DrawMenuBorders == DRAW_MENU_BORDERS_OVERALL )
    {
        hilite = NO_HILITE_OUTLINE|LEFT_HILITE|RIGHT_HILITE ;
        if( get_flags( item->flags, AS_MenuItemFirst ) )
            hilite |= TOP_HILITE ;
        if( get_flags( item->flags, AS_MenuItemLast ) )
            hilite |= BOTTOM_HILITE ;
        fhilite = hilite ;
    }else if( look->DrawMenuBorders == DRAW_MENU_BORDERS_FOCUSED_ITEM )
        fhilite = DEFAULT_MENU_HILITE;
    else if ( Scr.Look.DrawMenuBorders == DRAW_MENU_BORDERS_O_AND_F )
    {
        hilite = NO_HILITE_OUTLINE|LEFT_HILITE|RIGHT_HILITE ;
        if( get_flags( item->flags, AS_MenuItemFirst ) )
            hilite |= TOP_HILITE ;
        if( get_flags( item->flags, AS_MenuItemLast ) )
            hilite |= BOTTOM_HILITE ;
        fhilite = DEFAULT_MENU_HILITE ;
    }

    set_astbar_hilite( item->bar, BAR_STATE_UNFOCUSED, hilite );
    set_astbar_hilite( item->bar, BAR_STATE_FOCUSED, fhilite );

    if( get_flags( item->flags, AS_MenuItemDisabled ) )
    {
        set_astbar_composition_method( item->bar, BAR_STATE_UNFOCUSED, Scr.Look.menu_scm );
        set_astbar_composition_method( item->bar, BAR_STATE_FOCUSED, Scr.Look.menu_scm );
    }else
    {
        set_astbar_composition_method( item->bar, BAR_STATE_UNFOCUSED, Scr.Look.menu_icm );
        set_astbar_composition_method( item->bar, BAR_STATE_FOCUSED, Scr.Look.menu_hcm );
    }


    return True;
}

static void
render_asmenu_bars( ASMenu *menu, Bool force )
{
    int i = menu->items_num ;
    Bool rendered = False;
    if( menu->main_canvas->height > 1 && menu->main_canvas->width > 1 )
    {
        START_LONG_DRAW_OPERATION;
        for( i = 0 ; i < menu->items_num ; ++i )
        {
            int prev_y = -10000 ;
            register ASTBarData *bar = menu->items[i].bar ;
            if( bar->win_y >= (int)(menu->main_canvas->height) )
                continue;

/*                update_astbar_transparency (bar, menu->main_canvas, False); */

                  LOCAL_DEBUG_OUT( "bar %p needs_rendering? %s, visible? %s, lower ? %s", bar,
                                           DoesBarNeedsRendering(bar)?"yes":"no",
                                           (bar->win_y + (int)(bar->height) > 0)?"yes":"no",
                                           ((int)(bar->win_y) > prev_y)?"yes":"no");
            if( (force || DoesBarNeedsRendering(bar)) &&
                bar->win_y + (int)(bar->height) > 0 &&
                (int)(bar->win_y) > prev_y )
            {
                if( render_astbar( bar, menu->main_canvas ) )
                    rendered = True ;
            }
            prev_y = bar->win_y ;
        }
            if( menu->visible_items_num < menu->items_num )
            {
                  if( force || DoesBarNeedsRendering(menu->scroll_up_bar))
                  if( render_astbar( menu->scroll_up_bar, menu->main_canvas ) )
                  rendered = True ;
                  if( force || DoesBarNeedsRendering(menu->scroll_down_bar))
                  if( render_astbar( menu->scroll_down_bar, menu->main_canvas ) )
                  rendered = True ;
            }
        STOP_LONG_DRAW_OPERATION;
            LOCAL_DEBUG_OUT( "%s menu items rendered!", rendered?"some":"none" );
        if( rendered )
        {
            update_canvas_display( menu->main_canvas );
            ASSync(False);
                  menu->rendered = True ;
        }
    }
}

/*************************************************************************/
/* midium level ASMenu functionality :                                      */
/*************************************************************************/
static unsigned int
extract_recent_subitems( MenuData *md, MenuDataItem **subitems, unsigned int max_subitems )
{
    int used = 0 ;
    MenuDataItem *smdi = md->first ;
    while( smdi != NULL )
    {
        if( smdi->last_used_time > 0 )
        {
            if( used < max_subitems )
            {
                subitems[used] = smdi ;
                ++used ;
            }else
            {
                register int i = used ;
                while( --i >= 0 )
                    if( subitems[i]->last_used_time  > smdi->last_used_time )
                    {
                        subitems[i] = smdi ;
                        break;
                    }
            }
        }
        smdi = smdi->next ;
    }/* while smdi */
    return used;
}

void
set_asmenu_data( ASMenu *menu, MenuData *md, Bool first_time )
{
    int items_num = md->items_num ;
    int i = 0 ;
    int real_items_num = 0;
    int max_icon_size  = 0;
    MenuDataItem **subitems = NULL ;


    if( menu->items_num < items_num )
    {
        menu->items = realloc( menu->items, items_num*(sizeof(ASMenuItem)));
        memset( &(menu->items[menu->items_num]), 0x00, (items_num-menu->items_num)*sizeof(ASMenuItem));
    }

    if( menu->title )
    {
        free( menu->title );
        menu->title = NULL;
    }

    if( items_num > 0 )
    {
        MenuDataItem *mdi = md->first ;

        if( md->recent_items > 0 )
            subitems = safecalloc( md->recent_items, sizeof(MenuDataItem*));

        for(mdi = md->first; real_items_num < items_num && mdi != NULL ; mdi = mdi->next )
            if( mdi->fdata->func == F_TITLE && menu->title == NULL )
            {
                menu->title = mystrdup( mdi->item );
            }else
            {
                ASMenuItem *item = &(menu->items[real_items_num]);
                set_asmenu_item_data( item, mdi );

                ++real_items_num;
                if( mdi->fdata->func == F_POPUP && item->submenu != NULL && subitems != NULL )
                {
                    int used = extract_recent_subitems( item->submenu, subitems, md->recent_items );
                    if( used > 0 )
                    {
                        items_num += used ;
                        if( menu->items_num < items_num )
                        {
                            int to_zero = max(real_items_num,menu->items_num);
                            menu->items = realloc( menu->items, items_num*(sizeof(ASMenuItem)));
                            memset( &(menu->items[to_zero]), 0x00, (items_num-to_zero)*sizeof(ASMenuItem));
                        }
                        for( i = 0 ; i < used ; ++i )
                        {
                            set_asmenu_item_data( &(menu->items[real_items_num]), subitems[i] );
                            subitems[i] = NULL ;
                            set_flags( menu->items[real_items_num].flags, AS_MenuItemSubitem );
                            ++real_items_num;
                        }
                    }
                }
            }
        if( real_items_num > 0 )
        {
            for( i = 0 ; i < real_items_num ; ++i )
            {
                register ASMenuItem *item = &(menu->items[i]);
                if( item->icon )
                    if( item->icon->width > max_icon_size )
                        max_icon_size = item->icon->width ;
            }
            set_flags( menu->items[0].flags, AS_MenuItemFirst );
            set_flags( menu->items[real_items_num-1].flags, AS_MenuItemLast );
        }
        if( subitems )
            free( subitems );
    }
    menu->icon_space = MIN(max_icon_size,(Scr.MyDisplayWidth>>3));
    /* if we had more then needed tbars - destroy the rest : */
    if( menu->items )
    {
        i = menu->items_num ;
        while( --i >= real_items_num )
            free_asmenu_item(&(menu->items[i]));
    }
    menu->items_num = real_items_num ;
    menu->top_item = 0 ;
      if( first_time )
      {
          menu->selected_item = 0 ;
      if( menu->items && menu->items[0].bar )
            set_astbar_focused( menu->items[0].bar, menu->main_canvas, True );
      }else
            menu->selected_item = -1 ;
    menu->pressed_item = -1;
}

void
set_menu_scroll_bar_look( ASTBarData *bar, MyLook *look, Bool up )
{
      ASFlagType hilite = 0, fhilite = 0;

      bar->h_spacing = DEFAULT_MENU_SPACING ;
    bar->h_border = DEFAULT_MENU_ITEM_HBORDER ;
    bar->v_border = DEFAULT_MENU_ITEM_VBORDER ;

    delete_astbar_tile( bar, -1 );
    /* now readd it as proper type : */
    if( look->MenuArrow )
        add_astbar_icon( bar, 7, 0, up?FLIP_VERTICAL:FLIP_VERTICAL|FLIP_UPSIDEDOWN,
                             ALIGN_CENTER|RESIZE_H|RESIZE_H_SCALE, look->MenuArrow->image );
    else
        add_astbar_label( bar, 7, 0, 0, ALIGN_CENTER, 5, 5, "...", AS_Text_ASCII );

      set_astbar_style_ptr( bar, BAR_STATE_UNFOCUSED, look->MSMenu[MENU_BACK_ITEM] );
    set_astbar_style_ptr( bar, BAR_STATE_FOCUSED,   look->MSMenu[MENU_BACK_HILITE] );

      if( look->DrawMenuBorders == DRAW_MENU_BORDERS_ITEM )
        fhilite = hilite = DEFAULT_MENU_HILITE;
    else if ( Scr.Look.DrawMenuBorders == DRAW_MENU_BORDERS_OVERALL )
    {
        hilite = NO_HILITE_OUTLINE|LEFT_HILITE|RIGHT_HILITE|(up?TOP_HILITE:BOTTOM_HILITE) ;
        fhilite = hilite ;
    }else if( look->DrawMenuBorders == DRAW_MENU_BORDERS_FOCUSED_ITEM )
        fhilite = DEFAULT_MENU_HILITE;
    else if ( Scr.Look.DrawMenuBorders == DRAW_MENU_BORDERS_O_AND_F )
    {
        hilite = NO_HILITE_OUTLINE|LEFT_HILITE|RIGHT_HILITE|(up?TOP_HILITE:BOTTOM_HILITE) ;
        fhilite = DEFAULT_MENU_HILITE ;
    }

    set_astbar_hilite( bar, BAR_STATE_UNFOCUSED, hilite );
    set_astbar_hilite( bar, BAR_STATE_FOCUSED, fhilite );

    set_astbar_composition_method( bar, BAR_STATE_UNFOCUSED, Scr.Look.menu_icm );
    set_astbar_composition_method( bar, BAR_STATE_FOCUSED, Scr.Look.menu_hcm );
}

void
set_asmenu_look( ASMenu *menu, MyLook *look )
{
    int i ;
    unsigned int max_width = 0, max_height = 0;
    int display_size ;

    menu->arrow_space = look->MenuArrow?look->MenuArrow->width:DEFAULT_ARROW_SIZE ;

      set_menu_scroll_bar_look( menu->scroll_up_bar, look, True );
      set_menu_scroll_bar_look( menu->scroll_down_bar, look, False );

    i = menu->items_num ;
    while ( --i >= 0 )
    {
        unsigned int width, height ;
        register ASTBarData *bar;
        set_asmenu_item_look( &(menu->items[i]), look, menu->icon_space, menu->arrow_space );
        if( (bar= menu->items[i].bar) != NULL )
        {
            width = calculate_astbar_width( bar );
            height = calculate_astbar_height( bar );
LOCAL_DEBUG_OUT( "i(%d)->bar(%p)->size(%ux%u)", i, bar, width, height );
            if( width > max_width )
                max_width = width ;
            if( height > max_height )
                max_height = height ;
        }
    }
    /* some sanity checks : */

    if( max_height > MAX_MENU_ITEM_HEIGHT )
        max_height = MAX_MENU_ITEM_HEIGHT ;
    if( max_height == 0 )
        max_height = 1 ;
      /* we want height to be even at all times */
      max_height = ((max_height+1)/2)*2;

    if( max_width > MAX_MENU_WIDTH )
        max_width = MAX_MENU_WIDTH ;
    if( max_width == 0 )
        max_width = 1 ;
    menu->item_width = max_width ;
    menu->item_height = max_height ;

    display_size = max_height * menu->items_num ;
      menu->scroll_bar_size = max_height/2 ;

    if( display_size > MAX_MENU_HEIGHT )
    {
        menu->visible_items_num = MAX_MENU_HEIGHT/max_height;
        display_size = menu->visible_items_num* max_height ;  /* important! */
            display_size += 2*menu->scroll_bar_size ;  /* we'll need to render two more scroll bars */
    }else
        menu->visible_items_num = display_size / max_height ;

    /* setting up desired size  - may or maynot be overriden by user actions -
     * so we'll use ConfigureNotify events later on to keep ourselves up to date*/
    if( menu->owner != NULL )
        resize_canvas( menu->main_canvas, max_width, display_size );
    menu->optimal_width  = max_width ;
    menu->optimal_height = display_size ;

    if( menu->top_item > menu->items_num - menu->visible_items_num )
        menu->top_item = menu->items_num - menu->visible_items_num ;

   set_astbar_size( menu->scroll_up_bar, max_width, menu->scroll_bar_size );
   set_astbar_size( menu->scroll_down_bar, max_width, menu->scroll_bar_size );

      i = menu->items_num ;
    while ( --i >= 0 )
        set_astbar_size( menu->items[i].bar, max_width, max_height );
    ASSync(False);
}

static void
set_menu_item_used( ASMenu *menu, MenuDataItem *mdi )
{
    MenuData *md = FindPopup( menu->name, False );
    if( md && md->magic == MAGIC_MENU_DATA )
    {
        register MenuDataItem *i = md->first ;
        while( i != NULL )
        {
            if( i == mdi )
            {
                i->last_used_time = time(NULL);
                break;
            }
            i = i->next ;
        }
    }

}



void set_asmenu_scroll_position( ASMenu *menu, int pos );

void
select_menu_item( ASMenu *menu, int selection )
{
LOCAL_DEBUG_CALLER_OUT( "%p,%d", menu, selection );
    if( AS_ASSERT(menu) || menu->items_num == 0 )
        return;
    if( selection < 0 )
        selection = 0 ;
    else if( selection >= (int)menu->items_num )
        selection = menu->items_num - 1 ;

    if( selection != menu->selected_item )
    {
        close_asmenu_submenu( menu );
            if( menu->selected_item >= 0 )
            set_astbar_focused( menu->items[menu->selected_item].bar, menu->main_canvas, False );
    }
    set_astbar_focused( menu->items[selection].bar, menu->main_canvas, True );
    menu->selected_item = selection ;

    if( selection < menu->top_item )
        set_asmenu_scroll_position( menu, MAX(selection-1, 0) );
    else if( selection >= menu->top_item + menu->visible_items_num )
        set_asmenu_scroll_position( menu, (selection-menu->visible_items_num)+1);
    render_asmenu_bars(menu, False);
}

void
set_asmenu_scroll_position( ASMenu *menu, int pos )
{
    int curr_y ;
    int i ;
      int first_item, last_item ;

LOCAL_DEBUG_CALLER_OUT( "%p,%d", menu, pos );
    if( AS_ASSERT(menu) || menu->items_num == 0 )
        return;
      first_item = (pos < 0)?0:pos ;

      last_item =first_item + menu->visible_items_num-1 ;
      if( last_item >= menu->items_num )
      {
            last_item = menu->items_num-1 ;
            first_item = max(0, menu->items_num - menu->visible_items_num);
      }

      for( i = 0 ; i < first_item ; ++i )
            move_astbar( menu->items[i].bar, menu->main_canvas, 0, -menu->item_height );
      for( i = last_item+1 ; i < menu->items_num ; ++i )
            move_astbar( menu->items[i].bar, menu->main_canvas, 0, -menu->item_height );

      curr_y = 0 ;
      if( first_item > 0 || last_item < menu->items_num-1 )
      {
            move_astbar( menu->scroll_up_bar, menu->main_canvas, 0, curr_y );
            curr_y += menu->scroll_bar_size ;
      }else
      {
            move_astbar( menu->scroll_up_bar, menu->main_canvas, 0, -menu->scroll_bar_size );
            move_astbar( menu->scroll_up_bar, menu->main_canvas, 0, -menu->scroll_bar_size );
      }

      for( i = first_item ; i <= last_item ; ++i )
      {
            move_astbar( menu->items[i].bar, menu->main_canvas, 0, curr_y );
        curr_y += menu->item_height ;
      }
      if( first_item > 0 || last_item < menu->items_num-1 )
            move_astbar( menu->scroll_down_bar, menu->main_canvas, 0, curr_y );

LOCAL_DEBUG_OUT("adj_pos(%d)->curr_y(%d)->items_num(%d)->vis_items_num(%d)->sel_item(%d)", first_item, curr_y, menu->items_num, menu->visible_items_num, menu->selected_item);

      menu->top_item = first_item ;

      if( menu->selected_item < menu->top_item )
        select_menu_item( menu, menu->top_item );
    else if( menu->selected_item >= 0 )
      {
            if( menu->visible_items_num > 0 && menu->selected_item >= (int)menu->top_item + (int)menu->visible_items_num )
        select_menu_item( menu, menu->top_item + menu->visible_items_num - 1 );
      }
    render_asmenu_bars(menu, False);
}

static inline void
run_item_submenu( ASMenu *menu, int item )
{
LOCAL_DEBUG_CALLER_OUT( "%p, %d, submenu(%p)", menu, item, menu->items[item].submenu );
    if( menu->items[item].submenu )
    {
        int x = menu->main_canvas->root_x+(int)menu->main_canvas->bw ;
        int y ;
        ASQueryPointerRootXY( &x, &y );
        x -= 5 ;
#if 0
        if( x  < menu->main_canvas->root_x + (int)(menu->item_width/3) )
        {
            int max_dx = Scr.MyDisplayWidth / 40 ;
            if( menu->main_canvas->root_x + (int)(menu->item_width/3) - x  < max_dx )
                x = menu->main_canvas->root_x + (int)(menu->item_width/3) ;
            else
                x += max_dx ;
        }
#endif
        y = menu->main_canvas->root_y+(menu->item_height*(item+1-(int)menu->top_item)) - 10 ;
/*    if( x > menu->main_canvas->root_x+menu->item_width-5 )
          x = menu->main_canvas->root_x+menu->item_width-5 ;
*/      close_asmenu_submenu( menu );
        menu->submenu = run_submenu( menu, menu->items[item].submenu, x, y);
    }
}

void
press_menu_item( ASMenu *menu, int pressed, Bool keyboard_event )
{
LOCAL_DEBUG_CALLER_OUT( "%p,%d", menu, pressed );

    if( AS_ASSERT(menu) || menu->items_num == 0 )
        return;

    if( pressed >= (int)menu->items_num )
        pressed = menu->items_num - 1 ;

    if( menu->pressed_item >= 0 )
        set_astbar_pressed( menu->items[menu->pressed_item].bar, menu->main_canvas, False );

    if( pressed >= 0 )
    {
        if( get_flags(menu->items[pressed].flags, AS_MenuItemDisabled) )
            pressed = -1 ;
        else
        {
                  if( keyboard_event ) 
                  {   /* don't press if keyboard selection */
                        if( pressed != menu->selected_item )
                              select_menu_item( menu, pressed );

                  }else if( pressed != menu->selected_item )
            {
                set_astbar_pressed( menu->items[pressed].bar, NULL, True );/* don't redraw yet */
                select_menu_item( menu, pressed );  /* this one updates display already */
            }else
                set_astbar_pressed( menu->items[pressed].bar, menu->main_canvas, True );
            set_menu_item_used( menu, menu->items[pressed].source );
                  
                  if( menu->items[pressed].submenu != NULL ) 
                  {
                        if( keyboard_event ) 
                        {
                              int x = menu->main_canvas->root_x+(int)menu->main_canvas->bw+menu->main_canvas->width-(menu->arrow_space + DEFAULT_MENU_SPACING) ;
                              int y = menu->main_canvas->root_y+(int)menu->main_canvas->bw ;
                              y += menu->items[pressed].bar->win_y + menu->items[pressed].bar->height - 5 ;
                              XWarpPointer (dpy, None, Scr.Root, 0, 0, 0, 0, x, y);
                        }             
                  run_item_submenu( menu, pressed );
                  }
        }
    }
    render_asmenu_bars(menu, False);
LOCAL_DEBUG_OUT( "pressed(%d)->old_pressed(%d)->focused(%d)", pressed, menu->pressed_item, menu->focused );
    if( menu->focused )
      {     
            int item_no = keyboard_event?pressed:menu->pressed_item ;
      
            /* if keyboard event we exec item on press event, and on release event otherwise */ 
            if( item_no >= 0 && (pressed < 0 || keyboard_event))   
      {
            ASMenuItem *item = &(menu->items[item_no]);
            if( !get_flags(item->flags, AS_MenuItemDisabled) &&
                   item->fdata->func != F_POPUP )
            {
                  set_menu_item_used( menu, item->source );
                  ExecuteFunctionForClient( item->fdata, menu->client_window );
                  if( !menu->pinned )
                  close_asmenu( &ASTopmostMenu );
            }
      }
      }
      if( keyboard_event )
            menu->pressed_item = -1 ;
      else
      menu->pressed_item = pressed ;
}
/*************************************************************************/
/* Menu event handlers  - ASInternalWindow interface :                   */
/*************************************************************************/
void
menu_register_subwindows( struct ASInternalWindow *asiw )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
    if( menu != NULL && menu->magic == MAGIC_ASMENU )
    {
        register_aswindow( menu->main_canvas->w, asiw->owner );
    }
}

void
on_menu_moveresize( ASInternalWindow *asiw, Window w )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
    if( menu != NULL && menu->magic == MAGIC_ASMENU )
    {   /* handle config change */
        ASFlagType changed = handle_canvas_config( menu->main_canvas );
        register int i = menu->items_num ;
LOCAL_DEBUG_OUT( "changed(%lX)->main_width(%d)->main_height(%d)->item_height(%d)", changed, menu->main_canvas->width, menu->main_canvas->height, menu->item_height);
        if( get_flags( changed, CANVAS_RESIZED) )
        {
            if( get_flags( changed, CANVAS_WIDTH_CHANGED) )
            {
                while ( --i >= 0 )
                    set_astbar_size(menu->items[i].bar, menu->main_canvas->width, menu->item_height);
                        set_astbar_size(menu->scroll_up_bar, menu->main_canvas->width, menu->scroll_bar_size);
                        set_astbar_size(menu->scroll_down_bar, menu->main_canvas->width, menu->scroll_bar_size);
            }
            if( get_flags( changed, CANVAS_HEIGHT_CHANGED) )
            {
                menu->visible_items_num = menu->main_canvas->height / menu->item_height ;
                        if( menu->visible_items_num < menu->items_num )
                              menu->visible_items_num = (menu->main_canvas->height - (menu->scroll_bar_size*2) )/ menu->item_height ;
LOCAL_DEBUG_OUT( "update_canvas_display via set_asmenu_scroll_position from move_resize %s", "");
                set_asmenu_scroll_position( menu, menu->top_item );
            }
        }else if( get_flags( changed, CANVAS_MOVED) )
        {
            while ( --i >= 0 )
                update_astbar_transparency(menu->items[i].bar, menu->main_canvas, False);
                  update_astbar_transparency(menu->scroll_up_bar, menu->main_canvas, False);
                  update_astbar_transparency(menu->scroll_down_bar, menu->main_canvas, False);
        }
        if( changed != 0 )
            render_asmenu_bars(menu, get_flags( changed, CANVAS_RESIZED));
    }
}


/* fwindow looses/gains focus : */
void
on_menu_hilite_changed( ASInternalWindow *asiw, ASMagic *data, Bool focused )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
LOCAL_DEBUG_CALLER_OUT( "%p, %p, %d", asiw, data, focused );
    if( menu != NULL && menu->magic == MAGIC_ASMENU )
    {
        /* TODO : hilite/unhilite selected item, and
         * withdraw non-pinned menu if it has no submenus */
        menu->focused = focused ;
    }
}

/* ButtonPress/Release event on one of the contexts : */
void
on_menu_pressure_changed( ASInternalWindow *asiw, int pressed_context )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
LOCAL_DEBUG_CALLER_OUT( "%p,%p,0x%X", asiw, menu, pressed_context );
    if( menu != NULL && menu->magic == MAGIC_ASMENU )
    {
        /* press/depress menu item, possibly change the selection,
         * and run the function when item is depressed */
        if( pressed_context )
        {
            int px = 0, py = 0 ;
            ASQueryPointerWinXY( menu->main_canvas->w, &px, &py );
LOCAL_DEBUG_OUT( "pointer(%d,%d)", px, py );
            if( px >= 0 && px < menu->main_canvas->width &&  py >= 0 && py < menu->main_canvas->height )
            {
                        int pressed = -1 ;
                        if( menu->visible_items_num < menu->items_num )
                        {
                              if( py < menu->scroll_bar_size )
                              {
                                    set_asmenu_scroll_position( menu, menu->top_item-1 );
                              }else if( py > menu->scroll_down_bar->win_y )
                              {
                                    set_asmenu_scroll_position( menu, menu->top_item+1 );
                              }else
                              {
                                    pressed = (py - menu->scroll_bar_size)/menu->item_height ;
                              }
                              if( pressed < 0 )
                                    return ;
                        }else
                  pressed = py/menu->item_height ;
                        pressed += menu->top_item ;
                if( pressed != menu->pressed_item )
                    press_menu_item( menu, pressed, False );
            }
        }else if( menu->pressed_item >= 0 )
        {
            press_menu_item(menu, -1, False );
        }
    }
}

/* Motion notify : */
void
on_menu_pointer_event( ASInternalWindow *asiw, ASEvent *event )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
    if( menu != NULL && menu->magic == MAGIC_ASMENU && event)
    {
        /* change selection and maybe pop a submenu */
        XMotionEvent *xmev = &(event->x.xmotion);
        ASCanvas *canvas = menu->main_canvas ;
            int pointer_x = event->x.xmotion.x_root, pointer_y = event->x.xmotion.y_root;
            int px, py ;
            XEvent tmp_e ;
            if( ASCheckTypedWindowEvent(canvas->w,LeaveNotify,&tmp_e) )
            {
                  XPutBackEvent( dpy, &tmp_e );
                  return ;    /* pointer has moved into other window - ignore this event! */
            }
            /* must get current pointer position as MotionNotify events
               tend to accumulate while we are drawing and we start getting
               late with menu selection, creating an illusion of slowness */
            ASQueryPointerRootXY( &pointer_x, &pointer_y );
        px = pointer_x - (canvas->root_x+(int)canvas->bw);
            py = pointer_y - (canvas->root_y+(int)canvas->bw);

        if( px >= 0 && px < canvas->width &&  py >= 0 && py < canvas->height )
        {
                  int selection = -1 ;
                  if( menu->items_num > menu->visible_items_num )
                  {
                        Bool render = False ;
                        if( py < menu->scroll_bar_size )
                        {
                              if( set_astbar_focused( menu->scroll_up_bar, menu->main_canvas, True ) )
                                    render = True ;
                              if( set_astbar_focused( menu->scroll_down_bar, menu->main_canvas, False ) )
                                    render = True ;
                        }else
                        {
                              if( set_astbar_focused( menu->scroll_up_bar, menu->main_canvas, False ) )
                                    render = True ;
                              if( py > menu->scroll_down_bar->win_y )
                              {
                                    if( set_astbar_focused( menu->scroll_down_bar, menu->main_canvas, True ) )
                                          render = True ;
                              }else
                              {
                                    if( set_astbar_focused( menu->scroll_down_bar, menu->main_canvas, False ) )
                                          render = True ;
                                    else
                                          selection = (py-menu->scroll_bar_size)/menu->item_height ;
                              }
                        }
                        if( selection < 0 )
                        {
                              if( render )
                                    render_asmenu_bars(menu, False);
                              return;
                        }
                  }else
                        selection = py/menu->item_height ;
                  selection += menu->top_item ;

            if( selection != menu->selected_item )
            {
                if( xmev->state & ButtonAnyMask )
                    press_menu_item( menu, selection, False );
                else
                    select_menu_item( menu, selection );
            }
            if( px > canvas->width - ( menu->arrow_space + DEFAULT_MENU_SPACING ) &&
                menu->submenu == NULL )
                run_item_submenu( menu, selection );
        }
    }
}

void menu_destroy( ASInternalWindow *asiw );

/* KeyPress/Release : */
void
on_menu_keyboard_event( ASInternalWindow *asiw, ASEvent *event )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
    if( menu != NULL && menu->magic == MAGIC_ASMENU )
    {
            KeySym keysym = XLookupKeysym (&(event->x.xkey), 0);
          
            if (keysym == XK_Escape || 
                  keysym == XK_BackSpace ||
                  keysym == XK_Delete || 
                (keysym == XK_Left && menu->supermenu ) )
            {     
                  if( menu->supermenu != NULL ) 
                  {     
                        ASMenu *sm = menu->supermenu ; 
                        int x = sm->main_canvas->root_x + sm->main_canvas->bw; 
                        int y = sm->main_canvas->root_y + sm->main_canvas->bw;
                        if( sm->selected_item >= 0 && sm->selected_item < sm->items_num )
                        {
                              ASMenuItem *item = &(sm->items[sm->selected_item]);
                              x += item->bar->win_x + item->bar->width - (menu->arrow_space + DEFAULT_MENU_SPACING);
                              y += item->bar->win_y + item->bar->height/2 ;
                        }      
                        XWarpPointer (dpy, None, Scr.Root, 0, 0, 0, 0, x, y);
                  }
                  close_asmenu(&menu); 
/*            menu_destroy( asiw ); */
            return ;
            }else if ((keysym >= XK_A && keysym <= XK_Z) || /* Only consider alphabetic */
                          (keysym >= XK_a && keysym <= XK_z) ||
                          (keysym >= XK_0 && keysym <= XK_9))           /* ...or numeric keys     */
            {
                  int i ;
                  if (islower (keysym))
                        keysym = toupper (keysym);
                  LOCAL_DEBUG_OUT( "processing keysym [%c]", (char)keysym );
                  /* Search menu for matching hotkey */
                  for( i = 0 ; i < menu->items_num ; i++ )
                        if( menu->items[i].fdata->hotkey == keysym )
                        {
                              press_menu_item( menu, i, True );
                              return ;
                        }
                  for( i = 0 ; i < menu->items_num ; i++ )
                  {
                        char *n = menu->items[i].source->item ;
            
                        LOCAL_DEBUG_OUT( "name = %s", n?n:"(null)" );     
                        if( n != NULL && n[0] == keysym )
                        {
                              press_menu_item( menu, i, True );
                              return ;
                        }
                  }
            }else 
            {      
                  switch( keysym )
                  {
                        case XK_Page_Up:
                              break;
                        case XK_Page_Down:
                              break;
                        case XK_Up:
                        case XK_k:
                        case XK_p:
                              if( menu->selected_item > 0 )
                        select_menu_item( menu, menu->selected_item-1 );
                    break;
                        case XK_Tab :
                        case XK_Down:
                        case XK_n:
                        case XK_j:
                              if( menu->selected_item < menu->items_num )
                        select_menu_item( menu, menu->selected_item+1 );
                    break;
                        case XK_Right:
                        case XK_Return :
                        case XK_space :
                              if( menu->selected_item >= 0 ) 
                              {     
                                    if( menu->items[menu->selected_item].submenu || keysym != XK_Right ) 
                                    press_menu_item( menu, menu->selected_item, True );         
                              }
                  }      
            }     
    }
}

/* reconfiguration : */

void
on_menu_look_feel_changed( ASInternalWindow *asiw, ASFeel *feel, MyLook *look, ASFlagType what )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
    if( menu != NULL && menu->magic == MAGIC_ASMENU )
    {
            ASHints *hints ;
            unsigned int tbar_width ;

        if( menu->items )
        {
            register int i = menu->items_num;
            while ( --i >= 0 )
                free_asmenu_item( &(menu->items[i]));
            free( menu->items );
            menu->items = NULL ;
                  menu->items_num = 0 ;
        }

        if( get_flags( what, FEEL_CONFIG ) )
        {
            MenuData *md = FindPopup (menu->name, False);
            if( md == NULL )
            {
                menu_destroy( asiw );
                return ;
            }
            set_asmenu_data( menu, md, False );
        }
        set_asmenu_look( menu, look );
            hints = make_menu_hints( menu );
          estimate_titlebar_size( hints, &tbar_width, NULL );
          destroy_hints( hints, False );

      if( tbar_width > MAX_MENU_WIDTH )
            tbar_width = MAX_MENU_WIDTH ;
      if( tbar_width > menu->optimal_width )
            {
                  int i = menu->items_num ;
            menu->optimal_width = tbar_width ;
                  LOCAL_DEBUG_OUT( "menu_tbar_width = %d - resizing items!", tbar_width );
              resize_canvas( menu->main_canvas, tbar_width, menu->optimal_height );
                while ( --i >= 0 )
                  set_astbar_size( menu->items[i].bar, tbar_width, menu->item_height );
            }
          if( menu->items && menu->items[0].bar )
          set_astbar_focused( menu->items[0].bar, menu->main_canvas, False );
            menu->selected_item = -1 ;
            set_asmenu_scroll_position( menu, 0 );

//        render_asmenu_bars(menu);
    }
}

void
on_menu_root_background_changed( ASInternalWindow *asiw )
{
    ASMenu   *menu = (ASMenu*)(asiw->data) ;
    if( menu != NULL && menu->magic == MAGIC_ASMENU )
    { /* update transparency here */
        register int i = menu->items_num ;
        while ( --i >= 0 )
            update_astbar_transparency(menu->items[i].bar, menu->main_canvas, True);
            update_astbar_transparency(menu->scroll_up_bar, menu->main_canvas, True);
            update_astbar_transparency(menu->scroll_down_bar, menu->main_canvas, True);
      render_asmenu_bars(menu, False);
    }
}

/* destruction */
void
menu_destroy( ASInternalWindow *asiw )
{
    destroy_asmenu( (ASMenu**)&(asiw->data) );
}

ASHints *
make_menu_hints( ASMenu *menu )
{

    ASHints *hints = NULL ;

      if( menu == NULL )
            return NULL ;

      hints = safecalloc( 1, sizeof(ASHints) );

      /* normal hints : */
    hints->names[0] = mystrdup(menu->title?menu->title:menu->name);
    hints->names[1] = mystrdup(ASMENU_RES_CLASS);
    /* these are merely shortcuts to the above list DON'T FREE THEM !!! */
    hints->res_name  = hints->names[1];
    hints->res_class = hints->names[1];
    hints->icon_name = hints->names[0];

    hints->flags = AS_DontCirculate|
                   AS_SkipWinList|
                   AS_Titlebar|
                   AS_Border|
                   AS_Handles|
                   AS_AcceptsFocus|
                   AS_Gravity|
                   AS_MinSize|
                   AS_MaxSize|
                   AS_SizeInc;/*|AS_VerticalTitle ; */
    hints->protocols = AS_DoesWmTakeFocus ;
    hints->function_mask = ~(AS_FuncPopup|     /* everything else is allowed ! */
                             AS_FuncMinimize|
                             AS_FuncMaximize);

    hints->max_width  = MAX_MENU_WIDTH ;
    hints->max_height = MIN(MAX_MENU_HEIGHT,menu->items_num*menu->item_height);
    hints->width_inc  = 0 ;
    hints->height_inc = menu->item_height;
    hints->gravity = StaticGravity ;
    hints->border_width = BW ;
    hints->handle_width = BOUNDARY_WIDTH;

    hints->frame_name = mystrdup("ASMenuFrame");
    hints->mystyle_names[BACK_FOCUSED] = mystrdup(Scr.Look.MSMenu[MENU_BACK_HITITLE]->name);
    hints->mystyle_names[BACK_UNFOCUSED] = mystrdup(Scr.Look.MSMenu[MENU_BACK_TITLE]->name);
    hints->mystyle_names[BACK_STICKY] = mystrdup(Scr.Look.MSMenu[MENU_BACK_TITLE]->name);

    hints->disabled_buttons = 0;

    hints->min_width  = menu->optimal_width ;
    hints->min_height = menu->item_height ;

      return hints;
}

/*************************************************************************/
/* End of Menu event handlers:                                           */
/*************************************************************************/
void
show_asmenu( ASMenu *menu, int x, int y )
{
    ASStatusHints status ;
    ASHints *hints = make_menu_hints( menu );
      ASInternalWindow *asiw = safecalloc( 1, sizeof(ASInternalWindow));
    int gravity = StaticGravity ;
    unsigned int tbar_width = 0, tbar_height = 0 ;
    ASRawHints raw ;
    static char *ASMenuStyleNames[2] = {"ASMenu",NULL} ;
    ASDatabaseRecord *db_rec;

    asiw->data = (ASMagic*)menu;

    asiw->register_subwindows = menu_register_subwindows;
    asiw->on_moveresize = on_menu_moveresize;
    asiw->on_hilite_changed = on_menu_hilite_changed ;
    asiw->on_pressure_changed = on_menu_pressure_changed;
    asiw->on_pointer_event = on_menu_pointer_event;
    asiw->on_keyboard_event = on_menu_keyboard_event;
    asiw->on_look_feel_changed = on_menu_look_feel_changed;
      asiw->on_root_background_changed = on_menu_root_background_changed ;
    asiw->destroy = menu_destroy;

      db_rec = fill_asdb_record (Database, &(ASMenuStyleNames[0]), NULL, False);
      merge_asdb_hints ( hints, NULL, db_rec, NULL, HINT_GENERAL);

    estimate_titlebar_size( hints, &tbar_width, &tbar_height );

    if( tbar_width > MAX_MENU_WIDTH )
        tbar_width = MAX_MENU_WIDTH ;

    if( tbar_width > menu->optimal_width )
    {
        menu->optimal_width = tbar_width ;
        hints->min_width  = tbar_width ;
    }

    if( x <= MIN_MENU_X )
        x = MIN_MENU_X ;
    else if( x + menu->optimal_width + tbar_height> MAX_MENU_X )
    {
        x = MAX_MENU_X - menu->optimal_width ;
        gravity = EastGravity ;
    }else if( x + menu->optimal_width + tbar_height + menu->optimal_width> MAX_MENU_X )
            gravity = EastGravity ;

    if( y <= MIN_MENU_Y )
        y = MIN_MENU_Y ;
    else if( y + menu->optimal_height + tbar_height> MAX_MENU_Y )
    {
        y = MAX_MENU_Y - menu->optimal_height;
        gravity = (gravity == StaticGravity)?SouthGravity:SouthEastGravity ;
    }else if( y + menu->optimal_height + tbar_height + menu->optimal_height> MAX_MENU_Y )
        gravity = (gravity == StaticGravity)?SouthGravity:SouthEastGravity ;

    hints->gravity = gravity ;


    /* status hints : */
    memset( &status, 0x00, sizeof( ASStatusHints ) );
    status.flags = AS_StartPosition|
                   AS_StartPositionUser|
                   AS_StartSize|
                   AS_StartSizeUser|
                   AS_StartViewportX|
                   AS_StartViewportY|
                   AS_StartDesktop|
                   AS_StartLayer|
                   AS_StartsSticky;

    status.x = x;
    status.y = y;
    status.width = menu->optimal_width;
    status.height = menu->optimal_height;
    status.viewport_x = Scr.Vx;
    status.viewport_y = Scr.Vy;
    status.desktop = Scr.CurrentDesk;
    status.layer = AS_LayerMenu;

    /* now lets merge it with database record for ASMenu style - if it exists : */
    if( db_rec )
    {
        memset( &raw, 0x00, sizeof(raw) );
        raw.placement.x = status.x ;
        raw.placement.y = status.y ;
        raw.placement.width = menu->optimal_width ;
        raw.placement.height = menu->optimal_height ;
        raw.scr = ASDefaultScr ;
        raw.border_width = 0 ;

/*          LOCAL_DEBUG_OUT( "printing db record %p for names %p and db %p", pdb_rec, clean->names, db );
            print_asdb_matched_rec (NULL, NULL, Database, db_rec);
  */  
        merge_asdb_hints (hints, &raw, db_rec, &status, ASFLAGS_EVERYTHING);
            destroy_asdb_record( db_rec, False );
    }
    /* lets make sure we got everything right : */
    check_hints_sanity (ASDefaultScr, hints );
    check_status_sanity (ASDefaultScr, &status);

#if 0
    {
        int pointer_x = 0, pointer_y  = 0;
        if( ASQueryPointerRootXY(&pointer_x,&pointer_y) )
        {
            if( pointer_x< status.x || pointer_y < status.y ||
                pointer_x > status.x + status.width ||
                pointer_y > status.y + status.height  )
            {/* not likely to happen:  */
                XWarpPointer(dpy, Scr.Root, Scr.Root, pointer_x, pointer_y, 0, 0, status.x+5, status.y+5);
            }
        }
    }
#endif
    menu->owner = AddInternalWindow( menu->main_canvas->w, &asiw, &hints, &status );

    /* need to cleanup if we failed : */
    if( asiw )
    {
        if( asiw->data )
            destroy_asmenu( &menu );
        free( asiw );
    }
    if( hints )
        destroy_hints( hints, False );
}

/*************************************************************************/
/* high level ASMenu functionality :                                     */
/*************************************************************************/
ASMenu *
run_submenu( ASMenu *supermenu, MenuData *md, int x, int y )
{
    ASMenu *menu = NULL ;
    if( md )
    {
        menu = create_asmenu(md->name);
            menu->rendered = False ;
        set_asmenu_data( menu, md, True );
        set_asmenu_look( menu, &Scr.Look );
        /* will set scroll position when ConfigureNotify arrives */
        menu->supermenu = supermenu;
        show_asmenu(menu, x, y );
            MapConfigureNotifyLoop();
    }
    return menu;
}


void
run_menu_data( MenuData *md )
{
    int x = 0, y = 0;
      Bool persistent = (get_flags( Scr.Feel.flags, PersistentMenus ) || ASTopmostMenu == NULL );

    close_asmenu(&ASTopmostMenu);

      if( persistent )
      {
        if( md == NULL )
          return;

      if( !ASQueryPointerRootXY(&x,&y) )
      {
            x = (Scr.MyDisplayWidth*2)/3;
            y = (Scr.MyDisplayHeight*3)/ 4;
      }
      ASTopmostMenu = run_submenu(NULL, md, x, y );
      }
}

void
run_menu( const char *name, Window client_window )
{
    MenuData *md = FindPopup (name, False);
    run_menu_data( md );
      if( ASTopmostMenu ) 
            ASTopmostMenu->client_window = client_window ;
}


ASMenu *
find_asmenu( const char *name )
{
    if( name )
    {
        ASMenu *menu = ASTopmostMenu ;
        while( menu )
        {
            if( mystrcasecmp(menu->name, name) == 0 ||
                mystrcasecmp(menu->title, name) == 0)
                return menu;
            menu = menu->submenu ;
        }
    }
    return NULL;
}

ASMenu *
find_topmost_transient_menu( ASMenu *menu )
{
    if( menu && menu->supermenu && menu->supermenu->magic == MAGIC_ASMENU )
        if( !menu->supermenu->pinned )
            return find_topmost_transient_menu( menu->supermenu );
    return menu;
}

void
pin_asmenu( ASMenu *menu )
{
    if( menu )
    {
        ASMenu *menu_to_close = find_topmost_transient_menu( menu );

        close_asmenu_submenu( menu );
        if( menu == ASTopmostMenu )
            ASTopmostMenu = NULL ;
        else if( menu->supermenu &&
                 menu->supermenu->magic == MAGIC_ASMENU &&
                 menu->supermenu->submenu == menu )
            menu->supermenu->submenu = NULL ;
        if( menu_to_close != menu )
            close_asmenu( &menu_to_close );

        menu->pinned = True ;
        if( menu->owner )
        {
            clear_flags( menu->owner->hints->function_mask, AS_FuncPinMenu);
            redecorate_window( menu->owner, False );
            on_window_status_changed( menu->owner, True, True );
            if( Scr.Windows->hilited == menu->owner )
                on_window_hilite_changed (menu->owner, True);
        }
        close_asmenu_submenu( menu );
    }
}

Bool
is_menu_pinnable( ASMenu *menu )
{
    if( menu && menu->magic == MAGIC_ASMENU )
        return !(menu->pinned);
    return False;
}



Generated by  Doxygen 1.6.0   Back to index