/*-
 * Copyright (c) 2001 Jordan DeLong
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "menu.h"

/* per-screen structure */
static struct menuscr
{
	GC	menugc;
	GC	highlightgc;
	GC	redrawgc;
   int use_system_highlight;
   int use_xor_highlight;
} *menuscr;

/* font to use */
#ifdef I18N
static XFontSet	menufont;
static XFontSetExtents *menufont_extents;
#else
static XFontStruct	*menufont	= NULL;
#endif

/* decoration group */
static dgroup_t		*menu_dgroup	= NULL;

/* pixmaps */
static pixmap_t		*submenu_bullet	= NULL;

/* stacking layer */
int menu_stacklayer;

/* XContext to get menus */
XContext menu_context;

XColor bclr, fclr;
XColor hfclr;

/* initialize the menu system */
int menu_init( char *fontname,
               char *back_colour,
               char *fore_colour,
               char *highlight_colour,
               char *highlight_type,
               dgroup_t *dgroup,
               pixmap_t *bullet)
{
	XGCValues gcvalues;
	XGCValues highlight_gcvalues;
	XGCValues restore_gcvalues;
	int i, cnt, flags;

	/* this is used to get menu_t's from windows */
	menu_context = XUniqueContext();

	/*
	 * get the font structure, fall back on a semi-decent
	 * font, and if we can't even get that just use 'fixed',
	 * which should always be there.  If we can't use 'fixed'
	 * we give up.
	 */
	if (fontname)
   {
#ifdef I18N
		menufont = XLoadQueryFontSet(display, fontname);
#else
		menufont = XLoadQueryFont(display, fontname);
#endif
		if (menufont)
			goto gotfont;
		PWARN("unable to get requested menu_font, trying default");
	}
	PWARN("using default font");
#ifdef I18N
	menufont = XLoadQueryFontSet(display,
		"-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*");
#else
	menufont = XLoadQueryFont(display,
		"-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*");
#endif
	if (menufont)
		goto gotfont;
	PWARN("failed to load default font; trying 'fixed' as last resort");
#ifdef I18N
	menufont = XLoadQueryFontSet(display, "fixed");
#else
	menufont = XLoadQueryFont(display, "fixed");
#endif
	if (menufont)
		goto gotfont;
	PWARN("failed to load font 'fixed', giving up on menus");
	return -1;
gotfont:

#ifdef I18N
        menufont_extents = XExtentsOfFontSet(menufont);
#endif
	/* get the per-screen structure */
	cnt = ScreenCount(display);
	menuscr = calloc(cnt, sizeof(struct menuscr));
	if (!menuscr)
   {
		return -1;
   }


	/* fill in the structure */
	for (i = 0; i < cnt; i++)
   {
      if( !XParseColor( display,
                        DefaultColormap( display, i ),
                        fore_colour,
                        &fclr) )
      {
         gcvalues.foreground = WhitePixel(display, i);
      }
      else
      {
         if( !XAllocColor(display, DefaultColormap(display, i), &fclr) )
         {
            gcvalues.foreground = WhitePixel(display, i);
         }
         else
         {
            gcvalues.foreground = fclr.pixel;
         }
      }
      if( !XParseColor( display,
                        DefaultColormap( display, i ),
                        back_colour,
                        &bclr) )
      {
         gcvalues.background = BlackPixel(display, i);
      }
      else
      {
         if( !XAllocColor(display, DefaultColormap(display, i), &bclr) )
         {
            gcvalues.background = BlackPixel(display, i);
         }
         else
         {
            gcvalues.background = bclr.pixel;
         }
      }

      /* highlight stuff */
      if( strcmp( highlight_type, "system" ) == 0 )
      {
         menuscr[i].use_system_highlight = 1;
         menuscr[i].use_xor_highlight = 1;
      }
      else
      {
         highlight_gcvalues.background = BlackPixel(display, i);

         if( !XParseColor( display,
                           DefaultColormap( display, i ),
                           highlight_colour,
                           &hfclr) )
         {
            highlight_gcvalues.foreground = BlackPixel(display, i);
         }
         else
         {
            if( !XAllocColor(display, DefaultColormap(display, i), &hfclr) )
            {
               highlight_gcvalues.foreground = BlackPixel(display, i);
            }
            else
            {
               highlight_gcvalues.foreground = hfclr.pixel;
            }
         }

         if( strcmp( highlight_type, "xor" ) == 0 )
         {
            highlight_gcvalues.function = GXxor;
            highlight_gcvalues.subwindow_mode = IncludeInferiors;
            highlight_gcvalues.line_width = options.linewidth;
            flags = GCForeground | GCBackground | GCSubwindowMode | GCFunction | GCLineWidth;
            menuscr[i].use_xor_highlight = 1;
         }
         else
         {
            flags = GCForeground | GCBackground;
         }
         menuscr[i].highlightgc = XCreateGC( display, RootWindow(display, i),
                                             flags, &highlight_gcvalues);
         menuscr[i].use_system_highlight = 0;

         restore_gcvalues.foreground = gcvalues.background;
         menuscr[i].redrawgc = XCreateGC( display, RootWindow(display, i),
                                          GCForeground, &restore_gcvalues);
      }

#ifdef I18N
		menuscr[i].menugc = XCreateGC( display, RootWindow(display, i),
                                     GCForeground | GCBackground, &gcvalues);
#else
		gcvalues.font = menufont->fid;
		menuscr[i].menugc = XCreateGC( display, RootWindow(display, i),
                                     GCFont | GCForeground | GCBackground,
                                     &gcvalues);
#endif
	}

	menu_dgroup = dgroup;
	submenu_bullet = bullet;
	return 0;
}

/* shutdown the menu system */
void menu_shutdown()
{
	int i, cnt;

	cnt = ScreenCount(display);
	if (menuscr)
   {
		for (i = 0; i < cnt; i++)
      {
			if (menuscr[i].menugc)
         {
				XFreeGC(display, menuscr[i].menugc);
         }
         if( menuscr[i].use_system_highlight == 0 )
         {
            XFreeGC(display, menuscr[i].highlightgc );
         }
         if( menuscr[i].redrawgc )
         {
            XFreeGC(display, menuscr[i].redrawgc );
         }
      }
		free(menuscr);
	}

	if (menufont)
   {
#ifdef I18N
		XFreeFontSet(display, menufont);
#else
		XFreeFont(display, menufont);
#endif
   }
}

/* create a menu */
menu_t *menu_create()
{
	menu_t *menu;

	menu = calloc(1, sizeof(menu_t));
	if (!menu)
		return NULL;
	menu->forefather = menu;

	return menu;
}

/* add an entry to a menu, pos is the position to add it at */
menuent_t *menu_addent(menu_t *menu, int pos, int type, char *text, void *dat) {
	menuent_t *ent;
	menuent_t **tmp;
	menu_t **tmpsubmenus;
	int i;

	ent = calloc(1, sizeof(menuent_t));
	if (!ent)
		return NULL;
	ent->type = type;
	ent->text = text;

	/* get space for the new entry */
	tmp = realloc(menu->entries, (menu->entry_count + 1) * sizeof(menuent_t *));
	if (!tmp)
		goto free1;
	menu->entries = tmp;
	menu->entry_count++;

	/* submenus need some special attention */
	if (type == ET_SUBMENU)
   {
		tmpsubmenus = realloc(menu->submenus, (menu->submenu_count + 1) * sizeof(menu_t *));
		if (!tmpsubmenus)
			goto free2;
		menu->submenus = tmpsubmenus;
		menu->submenus[menu->submenu_count] = (menu_t *) dat;
		ent->dat.submenu = menu->submenu_count++;
		/* set the forefather pointer, and set it on all subwindows of this one */
		((menu_t *) dat)->forefather = menu->forefather;
		for (i = 0; i < ((menu_t *) dat)->submenu_count; i++)
			((menu_t *) dat)->submenus[i]->forefather = menu->forefather;
	} else
		ent->dat.dat = dat;

	/* move to make space if necessary, and put it in */
	if (pos == POS_LAST || pos >= menu->entry_count)
		pos = menu->entry_count - 1;
	else
		memmove(&menu->entries[pos + 1], &menu->entries[pos],
			(menu->entry_count - pos) * sizeof(menuent_t *));
	menu->entries[pos] = ent;

	return ent;

free2:
	menu->entry_count--;
free1:
	free(ent);
	return NULL;
}

/* dealloc a menu entry */
void menu_freeent(menuent_t *ent) {
	free(ent->text);

	/* free entry-type specific data */
	switch (ent->type) {
	case ET_RESTART:
	case ET_COMMAND:
		if (ent->dat.cmd)
			free(ent->dat.cmd);
		break;
	default:
		break;
	}

	free(ent);
}

/* resize a menu */
void menu_size(menu_t *menu)
{
	screen_t *screen;
	int width, height;
	int strwidth, i;

	width = MENU_MINWIDTH;
	height = MENU_YBORDER * 2;
	for (i = 0; i < menu->entry_count; i++) {
#ifdef I18N
		height += menufont_extents->max_ink_extent.height;
		strwidth = XmbTextEscapement(menufont, menu->entries[i]->text, strlen(menu->entries[i]->text));
#else
		height += menufont->ascent + menufont->descent;
		strwidth = XTextWidth(menufont, menu->entries[i]->text, strlen(menu->entries[i]->text));
#endif
		if (submenu_bullet && menu->entries[i]->type == ET_SUBMENU)
			strwidth += submenu_bullet->width;
		if (width < strwidth)
			width = strwidth;
	}
	width += MENU_XBORDER * 2;

	/* fill in the client_t for each screen */
	TAILQ_FOREACH(screen, &screen_list, s_list) {
		menu->client[screen->num]->width = width;
		menu->client[screen->num]->height = height;
		client_sizeframe(menu->client[screen->num]);
	}
}

/* realize a menu (called from post-init, i.e. start) */
int menu_realize(menu_t *menu)
{
	XSetWindowAttributes attr;
	clientflags_t flags;
	screen_t *screen;
	Window wnd;
	int i;

	menu->client = calloc(screen_count, sizeof(client_t *));
	if (!menu->client)
		return -1;

	menu->active_sub = calloc(screen_count, sizeof(menu_t *));
	if (!menu->active_sub)
		goto free;

	bzero(&flags, sizeof(clientflags_t));
	flags.internal = 1;
	flags.nofocus = 1;
	flags.noresize = 1;
	flags.noiconify = 1;
	flags.nodelete = 0; // bva: was: 1
	flags.sticky = 1;

	TAILQ_FOREACH(screen, &screen_list, s_list)
       {

          attr.background_pixel = bclr.pixel;

          wnd = XCreateWindow( display, RootWindow(display, screen->num),
                               0, 0, 50, 50, 0, CopyFromParent, CopyFromParent,
                               CopyFromParent, CWBackPixel, &attr );

          log( 5, "simple menu adding a client .......");
          menu->client[screen->num] = client_add(screen, wnd,
                                                 &flags, menu_dgroup);
          if (!menu->client[screen->num])
             return -1;

          /* set layer */
          menu->client[screen->num]->stacklayer = menu_stacklayer;

          XSaveContext(display, menu->client[screen->num]->frame,
                       menu_context, (XPointer) menu);
          XSelectInput( display, menu->client[screen->num]->window,
                        ExposureMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask );
          plugin_setcontext(plugin_this, menu->client[screen->num]->window);
          XMapWindow(display, menu->client[screen->num]->window);
       }

	menu_size(menu);

	/* realize the submenus */
	for (i = 0; i < menu->submenu_count; i++)
   {
		menu_realize(menu->submenus[i]);
   }

	return 0;

free:
	free(menu->client);
	return -1;
}

/* kill a menu, this may be before it's realized, so a lot is conditional */
void menu_delete(menu_t *menu) {
	int cnt, i;

	/* kill submenus */
	for (i = 0; i < menu->submenu_count; i++)
		menu_delete(menu->submenus[i]);
	if (menu->submenus)
		free(menu->submenus);

	/* get rid of the menu entries */
	for (i = 0; i < menu->entry_count; i++)
		if (menu->entries[i])
			menu_freeent(menu->entries[i]);
	if (menu->entries)
		free(menu->entries);

	/* free created clients */
	if (menu->client) {
		cnt = ScreenCount(display);
		for (i = 0; i < cnt; i++) {
			plugin_rmcontext(menu->client[i]->window);
			XDeleteContext(display, menu->client[i]->frame, menu_context);
			if (menu->client[i])
         {
				client_rm(menu->client[i]);
         }
		}
		free(menu->client);
	}

	/* free the active submenu pointers */
	if (menu->active_sub)
		free(menu->active_sub);

	free(menu);
}

/* draw a menu entry */
static void menu_drawent(menu_t *menu, client_t *client, int i, int y)
{
	int tmpx, tmpy;

	if (submenu_bullet && menu->entries[i]->type == ET_SUBMENU)
   {
		tmpx = client->width - submenu_bullet->width;
#ifdef I18N
		tmpy = y + (((menufont_extents->max_ink_extent.height) / 2) - submenu_bullet->height / 2);
#else
		tmpy = y + (((menufont->ascent + menufont->descent) / 2) - submenu_bullet->height / 2);
#endif

		XSetClipMask(display, menuscr[client->screen->num].menugc,
                   submenu_bullet->shapemasks[client->screen->num]);
		XSetClipOrigin(display, menuscr[client->screen->num].menugc, tmpx, tmpy);
		XCopyArea(display, submenu_bullet->pixmaps[client->screen->num], client->window,
                menuscr[client->screen->num].menugc, 0, 0, submenu_bullet->width,
                submenu_bullet->height, tmpx, tmpy);
		XSetClipMask(display, menuscr[client->screen->num].menugc, None);
	}
#ifdef I18N
	XmbDrawString(display, client->window, menufont, menuscr[client->screen->num].menugc,
                 MENU_XBORDER, y + menufont_extents->max_logical_extent.height * 4 / 5, menu->entries[i]->text, strlen(menu->entries[i]->text));
#else
	XDrawString(display, client->window,
               menuscr[client->screen->num].menugc,
               MENU_XBORDER,
               y + menufont->ascent, menu->entries[i]->text,
               strlen(menu->entries[i]->text));
#endif
}

/* draw a menu; entidx is for use during menu_interact */
void menu_expose(menu_t *menu, client_t *client, XExposeEvent *e)
{
	int i, y, first, last;

	/*
	 * be slightly intelligent about what lines to draw; it's
	 * probably not worth the time to worry about the width
	 * of the line getting drawn...
	 */
	y = MENU_YBORDER;
	last = first = -1;
	for (i = 0; i < menu->entry_count; i++) {
#ifdef I18N
		y += menufont_extents->max_ink_extent.height;
#else
		y += menufont->ascent + menufont->descent;
#endif
		if (y > e->y && first == -1)
			first = i - 1;
		if (y > e->y + e->height && last == -1)
			last = i;
	}

	/* now we draw the lines that need it */
	if (first < 0) first = 0;
	if (last == -1) last = menu->entry_count - 1;
#ifdef I18N
	y = MENU_YBORDER + (menufont_extents->max_ink_extent.height) * first;
#else
	y = MENU_YBORDER + (menufont->ascent + menufont->descent) * first;
#endif
	for (i = first; i < last + 1; i++)
   {
		menu_drawent(menu, client, i, y);
#ifdef I18N
		y += menufont_extents->max_ink_extent.height;
#else
		y += menufont->ascent + menufont->descent;
#endif
	}
}

/* perform an action for a menu entry */
static void menu_action(menu_t *menu, menuent_t *ent, int snum)
{
	switch (ent->type)
   {
      case ET_COMMAND:
         action_exec(menu->client[snum]->screen->num, ent->dat.cmd);
         break;
      case ET_RESTART:
         if (ent->dat.cmd)
            restart_bin = ent->dat.cmd;
         else
            restart_bin = binary_name;
         /* fall through */
      case ET_EXIT:
         restart_flag = 1;
         break;
      case ET_ABORT:
         /* this is useful for debug */
         if (!fork()) { abort(); exit(1); }
         break;
	}
}

/* open a menu */
static void menu_open(client_t *client, int x, int y)
{
	client->x = x;
	client->y = y;
	XMoveWindow(display, client->frame, client->x, client->y);
	workspace_add_client(client->screen->desktop->current_space, client);
	desktop_add_client(client);
	XMapWindow(display, client->frame);
	stacking_raise(client);
	client->state = NormalState;
}

/* close a menu */
static void menu_close(menu_t *menu, client_t *client)
{
	int snum = client->screen->num;

	/* close any open submenus */
	if (menu->active_sub[snum])
   {
		menu_close(menu->active_sub[snum], menu->active_sub[snum]->client[snum]);
		menu->active_sub[snum] = NULL;
	}
	if (client->workspace)
   {
		desktop_rm_client(client);
		workspace_rm_client(client);
	}
	XUnmapWindow(display, client->frame);
	client->state = WithdrawnState;
}

/* draw (or erase) a highlight */
static void drawhighlight( menu_t *menu, client_t *client, int idx, int erase_highlight )
{
   GC gc;

   if( erase_highlight )
   {
      if( menuscr[client->screen->num].use_system_highlight )
      {
         gc =  client->screen->xorgc;
      }
      else if( menuscr[client->screen->num].use_xor_highlight )
      {
         gc = menuscr[client->screen->num].highlightgc;
      }
      else
      {
         gc = menuscr[client->screen->num].redrawgc;
      }
   }
   else
   {
      if( menuscr[client->screen->num].use_system_highlight )
      {
         gc =  client->screen->xorgc;
      }
      else
      {
         gc = menuscr[client->screen->num].highlightgc;
      }
   }

#ifdef I18N
	XFillRectangle( display, client->window, gc, 2,
                   idx * (menufont_extents->max_ink_extent.height) + MENU_YBORDER,
                   client->width - 5, menufont_extents->max_ink_extent.height );
#else
   XFillRectangle( display, client->window,
                   gc, 2,
                   idx * (menufont->ascent + menufont->descent) + MENU_YBORDER,
                   client->width - 5, menufont->ascent + menufont->descent);
#endif

   if( !menuscr[client->screen->num].use_xor_highlight )
   {
#ifdef I18N
      menu_drawent(menu, client, idx, idx * (menufont_extents->max_ink_extent.height) + MENU_YBORDER);
#else
      menu_drawent(menu, client, idx, idx * (menufont->ascent + menufont->descent) + MENU_YBORDER);
#endif
   }
}

/* open submenus or close them as neccessary while dragging across */
static void passopen(menu_t *menu, client_t *client, int entidx, int *oldentidx, int snum)
{
   int xopen, yopen;
   client_t *tclient;

	if (menu->entries[entidx]->type == ET_SUBMENU) {
		if (menu->active_sub[snum] != menu->submenus[menu->entries[entidx]->dat.submenu]) {
			if (menu->active_sub[snum])
				menu_close(menu, menu->active_sub[snum]->client[snum]);
			menu->active_sub[snum] = menu->submenus[menu->entries[entidx]->dat.submenu];
#ifdef I18N
			menu_open(menu->active_sub[snum]->client[snum],
                   client->x + FULLWIDTH(client),
                   client->y + MENU_YBORDER + (entidx * (menufont_extents->max_ink_extent.height)));
#else

         tclient = menu->active_sub[snum]->client[snum];

         xopen = client->x + FULLWIDTH(client);
         yopen = client->y + MENU_YBORDER + (entidx * (menufont->ascent + menufont->descent));

         // NOTE: all this magic should be an option like: "keep on screen" AND put in a
         //  function!

         if( yopen + FULLHEIGHT(tclient) > client->screen->height )
         {
            yopen -= (yopen + FULLHEIGHT(tclient)) - client->screen->height;
         }

         if( xopen + FULLWIDTH(client) > client->screen->width )
         {
            log( 4, "We would be off the screen in the x direction" );
            xopen -= (xopen + FULLWIDTH(tclient)) - client->screen->width;
         }
         if( xopen < 0 )
         {
            log( 4, "We would be off the screen in the negative x direction" );
            xopen = 0;
         }

         menu_open( menu->active_sub[snum]->client[snum],
                    xopen,
                    yopen );

			/* menu_open( menu->active_sub[snum]->client[snum], */
/*                     client->x + FULLWIDTH(client), */
/*                     client->y + MENU_YBORDER + (entidx * (menufont->ascent + menufont->descent))); */
#endif
		}
		*oldentidx = -1;
	}
   else
   {
		drawhighlight(menu, client, entidx, 0);
		if (menu->active_sub[snum])
      {
			menu_close(menu, menu->active_sub[snum]->client[snum]);
			menu->active_sub[snum] = NULL;
		}
		*oldentidx = entidx;
	}
}

/* main menu operation routine, e == NULL if not from a keypress */
static void menu_interact(menu_t *menu, client_t *client, XButtonEvent *butev)
{
	XEvent event;
	XMotionEvent *e;
	menu_t *tmpmenu;
	Window lastsubwin, dumwin;
	int xpos, ypos, snum;
	int oldentidx, entidx, mask;
	int moving;

	/* grab the pointer */
	mask = ButtonReleaseMask | PointerMotionMask | ButtonMotionMask;
	if (XGrabPointer(display, client->screen->root, 0, mask, GrabModeAsync,
			GrabModeAsync, client->screen->root, None, CurrentTime) != GrabSuccess)
		return;
	stacking_raise(client);
	snum = client->screen->num;

	/* set the entry index var if we got a event structure */
	if (butev) {
#ifdef I18N
		oldentidx = entidx = (butev->y - MENU_YBORDER)
			/ (menufont_extents->max_ink_extent.height);
#else
		oldentidx = entidx = (butev->y - MENU_YBORDER)
			/ (menufont->ascent + menufont->descent);
#endif
		if (entidx >= 0 && entidx < menu->entry_count) {
			XSync(display, 0);
			while (XCheckMaskEvent(display, ExposureMask, &event))
         {
				event_handle(&event);
         }

         passopen(menu, client, entidx, &oldentidx, snum);

		} else {
			oldentidx = entidx = -1;
		}
	} else {
		oldentidx = entidx = -1;
	}

	moving = 0;
	lastsubwin = -1;
	mask |= ExposureMask;
	while (1) {
		XMaskEvent(display, mask, &event);

		switch (event.type) {
		case MotionNotify:
			e = &event.xmotion;
			moving = 1;

			/* see if we moved to another window */
			if (e->subwindow != lastsubwin && e->subwindow != client->frame) {
				lastsubwin = e->subwindow;
				if (oldentidx != -1)
					drawhighlight(menu, client, oldentidx, 1);
				if (e->subwindow == None
						|| XFindContext(display, e->subwindow, menu_context, (XPointer *) &tmpmenu) != 0) {
					oldentidx = entidx = -1;
					break;
				}
				if (menu->active_sub[snum] && menu->active_sub[snum]->client[snum]->frame != e->subwindow)
					menu_close(menu, menu->active_sub[snum]->client[snum]);
				menu = tmpmenu;
				client = menu->client[snum];
				oldentidx = -1;
			}

			/* get cordinates and erase old highlights, draw new if needed */
			XTranslateCoordinates(display, e->root, client->window, e->x_root, e->y_root,
				&xpos, &ypos, &dumwin);
			if (ypos >= client->height - MENU_YBORDER || ypos <= MENU_YBORDER || xpos <= MENU_XBORDER
					|| xpos >= client->width - MENU_XBORDER) {
				if (oldentidx != -1)
					drawhighlight(menu,client, oldentidx, 1);
				entidx = oldentidx = -1;
				break;
			}

#ifdef I18N
			entidx = (ypos - MENU_YBORDER) / (menufont_extents->max_ink_extent.height);
#else
			entidx = (ypos - MENU_YBORDER) / (menufont->ascent + menufont->descent);
#endif
			if (entidx != oldentidx) {
				if (oldentidx != -1)
					drawhighlight(menu,client, oldentidx, 1);
				passopen(menu, client, entidx, &oldentidx, snum);
			} else
				oldentidx = entidx;
			break;
		case ButtonRelease:
			XUngrabPointer(display, CurrentTime);
			if (oldentidx != -1)
				drawhighlight(menu,client, oldentidx, 1);
			if (entidx != -1) {
				if (menu->entries[entidx]->type == ET_SUBMENU)
					return;
				menu_action(menu, menu->entries[entidx], snum);

				/*
				 * for now, always close when doing an action.  at some point
				 * I may do break-off-able menus and such
				 */
				menu_close(menu->forefather, menu->forefather->client[snum]);
				return;
			}
			if (moving)
				menu_close(menu->forefather, menu->forefather->client[snum]);
			return;
		case Expose:
			event_handle(&event);
			break;
		}
	}
}

/* open or close a menu */
void menu_use(menu_t *menu, screen_t *screen)
{
	Window dumwin;
	client_t *client;
	int dumint, x, y;
   unsigned int dumuint;
   int yopen, xopen;

   log( 2, "menu_use" );

	client = menu->client[screen->num];

	if (client->state == WithdrawnState)
   {
		XQueryPointer( display, screen->root, &dumwin, &dumwin, &x, &y, &dumint, &dumint,
                     &dumuint );

      // x,y tell us where we are with the pointer
      // screen->width, screen->height tells us how big things are ...
      // client->width, client->height tells us how big the menu is ...
      log( 4, "we are at: %d,%d", x, y );
      log( 4, "opening a menu .....");

      //	menu_open( client, x - client->width / 2, y);


      // 1) we can adjust where this beast opens ...
      xopen = x - client->width / 2;
      yopen = y;

      // NOTE: all this magic should be an option like: "keep on screen"
      if( y + client->height > screen->height )
      {
         log( 4, "We would be off the screen in the y direction" );
         yopen = y - ((y + client->height + 10) - screen->height);
      }
      if( x + client->width > screen->width )
      {
         log( 4, "We would be off the screen in the x direction" );
         xopen = x - ((x + client->width + 10) - screen->width);
      }
      if( xopen < 0 )
      {
         log( 4, "We would be off the screen in the negative x direction" );
         xopen = 0;
      }

      menu_open( client, xopen, yopen );

      // 2) we can warp the menu ala windowmaker in here
		menu_interact( menu, client, NULL );
	}
   else
   {
		/* need an if it's moved thing */
		menu_close(menu, client);
	}
}

/* a click on a menu, go into interaction with the menu */
void menu_click(menu_t *menu, client_t *client, XButtonEvent *e)
{
	menu_interact(menu, client, e);
}
