/*********************************************************************
 *
 *         EZWGL, the EZ Widget and Graphics Library
 *
 *             Copyright (C) 1996, 1997  Maorong Zou
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/
/*
 *  June 1996.  Beta Release.
 *  Sept 1996.  Release Version 1.0
 *  Dec 1996.  Release Version 1.1 Beta
 *  April 1997.  Release Version 1.2
 *  November 1997.  Release Version 1.3
 */
/*******************************************************************
 *  
 *  EZ Widget library. 
 *              Event handling.
 */
#define _EZ_WIDGET_EVENT_C_

#include "EZ_Widget.h"
#include <X11/Xatom.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
/*******************************************************************
 *
 * Functions implemented in this file:
 */
void            EZ_WidgetDispatchEvent MY_ANSIARGS((XEvent *event));
void            EZ_HandleWidgetWindowEvent MY_ANSIARGS((EZ_Widget *widget, XEvent *event));
void            EZ_ServiceEvents MY_ANSIARGS((void));
void            EZ_WaitAndServiceNextEvent MY_ANSIARGS((void));
void            EZ_EventMainLoop MY_ANSIARGS((void));
void            EZ_InvokePrivateEventHandler MY_ANSIARGS((XEvent *event));
void            EZ_InitializeEventHT MY_ANSIARGS((void));
void            EZ_InsertGlobalKeyPressEvent  MY_ANSIARGS((EZ_Widget *widget,int modif, char *key));
void            EZ_RemoveGlobalKeyEvent  MY_ANSIARGS((int modi, char *key, EZ_Widget *widget));
void            EZ_RemoveAllKeyShortcutForWidget  MY_ANSIARGS((EZ_Widget *widget));
int             EZ_GetGlobalKeyEvent  MY_ANSIARGS((int mod, char *key, EZ_Widget *root, 
						   EZ_Widget **widget_ret, int *x_r, int *y_r));
void            EZ_TiePopupMenu  MY_ANSIARGS((EZ_Widget *widget, int btnNm, int where));
void            EZ_WarpPointerToWidgetWindow MY_ANSIARGS((EZ_Widget *wgt, int evt));

/*******************************************************************/
void            EZ_RemoveEvent  MY_ANSIARGS((XEvent *event));
int             EZ_CheckTimerEvents MY_ANSIARGS((void));
void            EZ_HandleGlobalKeyEvent MY_ANSIARGS((EZ_Widget *w, int x, int y));
void            EZ_TFEvents MY_ANSIARGS((void));
int             EZ_FilterEvent MY_ANSIARGS((XEvent *event));
void            EZ_WarpPointer MY_ANSIARGS((int xx, int yy));
/*******************************************************************/
Time            EZ_LastEventTime = CurrentTime;
/*******************************************************************
 *
 * Local Functions:
 */
static void     EZ_HandleGlobalKeyEventCarefully  MY_ANSIARGS((EZ_Widget *widget, XEvent *event, int x, int y));
static int      EZ_HASH_CHAR MY_ANSIARGS((char c));
static void     EZ_HideBubbleHelp MY_ANSIARGS((void));
static void     EZ_HandleClientMessage MY_ANSIARGS((EZ_Widget *widget, int etype, XEvent *xevent));

/*******************************************************************
 *
 * Local variables:
 */
#define EZ_GLOBALEVENT_HASH_SIZE  128  

typedef struct _EZ_GlobalEvent_{
  EZ_Widget         *widget;         /* to which widget this event is binded to */
  int               modifiers;       /* key modifiers */
  struct  _EZ_GlobalEvent_ *next;  /* the chain     */
} EZ_GlobalEvent;

static EZ_GlobalEvent EZ_GlobalKeyEvent[EZ_GLOBALEVENT_HASH_SIZE];
static int  PopupPosition[3];  /* where to post popup menus, one for each button */

/********************************************************************
 * for bubble help
 */
static struct timeval LastBubbleTime;
static Window LastBubbleWindow = (Window)NULL;

/********************************************************************/
void EZ_GetCurrentTime(tv)
     struct timeval *tv;
{
  (void) gettimeofday(tv,NULL);
}

/*******************************************************************
 *
 *  If there are any pending X events, service them. If there is none
 *  return.
 */
void  EZ_ServiceEvents()
{
  XEvent xevent;

  while(XPending(EZ_Display) != 0) 
    {
      XNextEvent(EZ_DisplayForWidgets,&xevent);
      if(EZ_FilterEvent(&xevent))
	{
	  EZ_InvokePrivateEventHandler(&xevent);
	  EZ_WidgetDispatchEvent(&xevent);
	}
    }
  (void) EZ_CheckTimerEvents();
  (void) EZ_CheckAppInputs(0);
}
/***************************************************************
 *
 *  Wait for the next event, service it if it arrives.
 */
void  EZ_WaitAndServiceNextEvent()
{
  XEvent xevent;
  while(1)
    {
      if(XPending(EZ_Display))
	{
	  XNextEvent(EZ_Display,&xevent);
	  if(EZ_FilterEvent(&xevent))
	    {
	      EZ_InvokePrivateEventHandler(&xevent);
	      EZ_WidgetDispatchEvent(&xevent);
	    }
	  break;
	}
      else
	{
	  if(EZ_CheckTimerEvents()) break;
	  if(EZ_CheckAppInputs(1000) != 0) /* -1 or got some file events */
	    EZ_SitForALittleBit(1000); 
	}
    }
}

/*******************************************************************
 *
 *  The main event loop. Not very useful.
 */

void EZ_EventMainLoop()
{
  XEvent event;
  while(1)
    {
      while(XPending(EZ_Display)) /* X events are of high priority */
	{
	  XNextEvent(EZ_Display,&event);
	  if(EZ_FilterEvent(&event))
	    {
	      EZ_InvokePrivateEventHandler(&event);
	      EZ_WidgetDispatchEvent(&event);
	    }
	}
      EZ_CheckTimerEvents();
      if(EZ_CheckAppInputs(1000) != 0)
	EZ_SitForALittleBit(1000);
    }
}

/**************************************************************************
 * Does nothing, just record the event time.
 */
int EZ_FilterEvent(event)
     XEvent *event;
{
  Time NewTime;

  switch (event->type) 
    {
    case KeyPress:
    case KeyRelease:
      NewTime = event->xkey.time;
      break;
    case ButtonPress:
    case ButtonRelease:
      NewTime = event->xbutton.time;
      break;
    case MotionNotify:
      NewTime = event->xmotion.time;
      break;
    case EnterNotify:
    case LeaveNotify:
      NewTime = event->xcrossing.time;
      break;
    case PropertyNotify:
      NewTime = event->xproperty.time;
      break;
    case SelectionClear:
      NewTime = event->xselectionclear.time;
      break;
    case SelectionRequest:
      NewTime = event->xselectionrequest.time;
      break;
    case SelectionNotify:
      NewTime = event->xselection.time;
      break;
    default:
      NewTime = CurrentTime;
      break;
    }
  if(NewTime > EZ_LastEventTime)   EZ_LastEventTime = NewTime;  
  return(1);
}

/**************************************************************************
 *
 *  Dispatch event to the approprate enent handler.
 *
 *  ConfigureNotify is a troublesome thing.  Resizing/moving a window generats it,
 *  internal configuration generates it as well; and there is no way to tell 
 *  which is which !!!!
 *
 *  Hack: Handle those ConfigureNotify which occur at least  1 second after the last
 *  one was handled. This seems to work. Usually, two user resizing cannot
 *  happen within 1 second.
 */
static long  Last_CN_HTime = 0; /* time at which the last ConfigureNotify was handled, in seconds */

void EZ_WidgetDispatchEvent(event)
     XEvent *event;
{
  EZ_Widget *widget, *widget_return;
  Window          win; 

  win = event->xany.window;
  if( (widget = EZ_LookupWidgetFromMappedHT(win)) == (EZ_Widget *)NULL)
    return;

  /*----------------------------------------------------------------------
   * for bubble help window does not responds to anything.
   */
  if(widget == EZ_BubbleHelp) return;
  /*-------------------------------------------------------------
   * First check if there are any active grab.
   *------------------------------------------------------------*/
  if(EZ_GrabedWidget)
    {
      /* if(EZ_WidgetIsViewable(EZ_GrabedWidget))*/
      if(EZ_LookupWidgetFromAllHT(EZ_GrabedWidget) &&
	 EZ_WidgetWindow(EZ_GrabedWidget) != None )
	{
	  int type = event->type;
	  int bnkcnt = 0;
	  switch(type)
	    {
	    case Expose:
	    case SelectionClear:
	    case ConfigureNotify:
	    case EnterNotify:
	    case LeaveNotify:
	    case FocusIn:
	    case FocusOut:
	    case MotionNotify:
	      break;
	    case ButtonPress:
	    case KeyPress:
	      bnkcnt=0;
	    default:
	      {
		EZ_Widget *tmp = widget;
		while(tmp && tmp != EZ_GrabedWidget)
		  tmp = EZ_WidgetParent(tmp);
		if(tmp != EZ_GrabedWidget)
		  {
		    EZ_BlinkWidget(EZ_GrabedWidget, bnkcnt);
		    return;
		  }
	      }
	      break;
	    }
	}
      else EZ_GrabedWidget = NULL;
    }
  

  /*-------------------------------------------------------------
   *  if this event is a KeyPress and it is binded to a global
   *  function, do it here. Otherwise, send it to the
   *  appropriate widget.  TEXT and ENTRY Widgets are special. 
   *  They handle key events themselves.
   *------------------------------------------------------------*/
  switch(event->type)
    {
    case UnmapNotify:
      if(EZ_WidgetParent(widget) == NULL)
	EZ_WidgetMapped(widget) = 0;
      break;
    case MapNotify:
      if(EZ_WidgetParent(widget) == NULL)
	{
	  EZ_WidgetMapped(widget) = 1;
	  EZ_DisplayWidget(widget);
	}
      break;
    case KeyPress:
      {
	int type = EZ_WidgetType(widget);
	switch(type)
	  {
	  case EZ_WIDGET_ITEXT:
	  case EZ_WIDGET_ENTRY:
	    /* case EZ_WIDGET_3D_CANVAS:*/
	    break;
	  default:
	    {
	      KeySym            keysym;
	      XComposeStatus    compose; 
	      char              tmpbuffer[8];
	      int               count;
	      int modifiers = (event->xkey.state & (ShiftMask | ControlMask | Mod1Mask));
	      event->xkey.state &= ~modifiers; 
	      count = XLookupString(&(event->xkey), tmpbuffer, 8, &keysym, &compose);
	      tmpbuffer[count] = '\0'; 
      
	      if(modifiers != 0)
		{
		  EZ_Widget   *root = widget;
		  int is_global_event = 0;

		  if(count != 0)
		    {
		      int xx, yy;
		      while(EZ_WidgetParent(root) != (EZ_Widget *)NULL) 
			root = EZ_WidgetParent(root);
		      is_global_event = EZ_GetGlobalKeyEvent(modifiers, tmpbuffer,
							     root, &widget_return, &xx, &yy);
		      if(is_global_event != 0)
			{
			  EZ_HandleGlobalKeyEvent(widget_return, xx, yy);
			  return;
			}
		    }
		}
	      /* restore xkey.state state */
	      event->xkey.state |= modifiers; 
	      
	      /* not binded to a global event */
	      switch(keysym)
		{
		case XK_Tab:  
#ifdef XK_KP_Tab
		case XK_KP_Tab:
#endif
		  {
		    int type = EZ_WidgetType(widget);
		    if(type != EZ_WIDGET_POPUP_MENU &&
		       type != EZ_WIDGET_MENU_TEAR_OFF_BAR &&
		       (type < EZ_WIDGET_MENU_SEPARATOR ||
			type > EZ_WIDGET_MENU_RADIO_BUTTON))
		      EZ_FocusNextWidget(widget, (modifiers & ShiftMask));
		  }
		break;
		case XK_Return: case XK_Linefeed: case XK_space:  
#ifdef XK_KP_Enter
		case XK_KP_Enter:
#endif
		  if(type != EZ_WIDGET_3D_CANVAS &&
		     widget == EZ_CurrentFocusingWidget &&
		     !(event->xkey.send_event))
		    {
		      EZ_HandleGlobalKeyEventCarefully(widget,event, 0, 0);
		      return;
		    }
		  break;
#ifdef KEY_MOVE_POINTER 
		case XK_Up: 
#ifdef XK_KP_Up
		case XK_KP_Up: 
#endif
		  if(modifiers && type != EZ_WIDGET_3D_CANVAS)
		    {
		      if(modifiers & ShiftMask) munit = 1;
		      else if(modifiers & ControlMask) munit = 16;
		      else munit = 4;
		      if(modifiers & Mod1Mask) munit = munit << 2;
		      EZ_WarpPointer(0,-munit);
		      return;
		    }
		  break;
		case XK_Down: 
#ifdef XK_KP_Down
		case XK_KP_Down:
#endif
		  if(modifiers && type != EZ_WIDGET_3D_CANVAS)
		    {
		      if(modifiers & ShiftMask) munit = 1;
		      else if(modifiers & ControlMask) munit = 16;
		      else munit = 4;
		      if(modifiers & Mod1Mask) munit = munit << 2;
		      EZ_WarpPointer(0,munit);
		      return;
		    }
		  break;
		case XK_Left: 
#ifdef XK_KP_Left
		case XK_KP_Left:
#endif
		  if(modifiers && type != EZ_WIDGET_3D_CANVAS)
		    {
		      if(modifiers & ShiftMask) munit = 1;
		      else if(modifiers & ControlMask) munit = 16;
		      else munit = 4;
		      if(modifiers & Mod1Mask) munit = munit << 2;
		      EZ_WarpPointer(-munit, 0);
		      return;
		    }
		  break;
		case XK_Right: 
#ifdef XK_KP_Right
		case XK_KP_Right:
#endif
		  if(modifiers && type != EZ_WIDGET_3D_CANVAS)
		    {
		      if(modifiers & ShiftMask) munit = 1;
		      else if(modifiers & ControlMask) munit = 16;
		      else munit = 4;
		      if(modifiers & Mod1Mask) munit = munit << 2;
		      EZ_WarpPointer(munit, 0);
		      return;
		    }
		  break;
#endif
		default:
		  break;
		}
	    } /* default */
	  }   /* switch */
      }
      break;
    case ButtonPress:
      {
	/*------------------------------------------
	 *  Search for globally bined mose event.
	 *  The only thing allowed is that a button
	 *  is bined to a popupmenu.
	 *------------------------------------------*/
	if((EZ_WidgetType(widget) != EZ_WIDGET_MENU_BUTTON) &&
	   (EZ_WidgetType(widget) != EZ_WIDGET_3D_CANVAS || 
	    EZ_3DCanvasReleaseMenuButton(widget) != 0))
	  {
	    /*----------------------------------------
	     * leave 3D CANVAS alone so that user
	     * supplied event handle will work
	     * properly
	     *-------------------------------------*/
	    EZ_GlobalEvent *tt =  &EZ_GlobalKeyEvent[EZ_GLOBALEVENT_HASH_SIZE-1];	  
	    EZ_Widget  *twidget = tt->widget;
	    int        which_button = tt->modifiers;  
	    if(event->xbutton.button == which_button &&
	       twidget != (EZ_Widget *)NULL &&
	       EZ_WidgetType(twidget) == EZ_WIDGET_POPUP_MENU)
	      {
		int where;
		if(EZ_WidgetBackground(widget))
		  EZ_WidgetParentBG(twidget) = &(EZ_WidgetBackground(widget));
		else
		  EZ_WidgetParentBG(twidget) = EZ_WidgetParentBG(widget);
		if(which_button == EZ_Btn1)
		  where = PopupPosition[0];
		else if(which_button == EZ_Btn2)
		  where = PopupPosition[1];
		else if(which_button == EZ_Btn3)
		  where = PopupPosition[2];
		else 
		  where = 0;
		EZ_DoPopup(twidget,where);
		return;
	      }
	  }
      }
      break;
      /*-----------------------------------------------------------------
       *  So this event is not binded to any global function.
       *----------------------------------------------------------------*/
    case ConfigureNotify:
      {
	/*----------------------------------------------------------------
	 *  Intercept ConfigureNotify events.  Tricky !!!
	 *------------------------------------------------------------*/  
	if(EZ_WidgetParent(widget) == (EZ_Widget *)NULL) /* a toplevel widget */
	  {
	    int x, y, w, h;
	    
	    x = event->xconfigure.x;
	    y = event->xconfigure.y;
	    w = event->xconfigure.width;
	    h = event->xconfigure.height;
	    
	    /* if widget is embeded. Send a message to its embeder inform
	     * it that the size of the embeded window may have been changed.
	     */
	    if(EZ_GetWidgetEmbededFlag(widget)) 
	      {
		Window cwin = EZ_WidgetDnDApplName(widget);
		Window pwin = EZ_WidgetDnDParent(widget);
		if(EZ_VerifyExistence(cwin) && EZ_WindowExist(pwin))
		  {
		    EZ_SendEmbedingMessage(EZ_EMBEDING_WINDOW_RESIZED_S,
					   EZ_DummyWindow, EZ_WidgetWindow(widget),
					   (unsigned long)widget, 
					   cwin, pwin, 0L, 
					   EZ_ApplicationSetupTime,
					   0,0,w,h,
					   EZ_WidgetAMinWidth(widget),EZ_WidgetAMinHeight(widget));
		  }
	      }
	    /* Must a user resize */
	    else if( w != EZ_WidgetWidth(widget) || h != EZ_WidgetHeight(widget)) 
	      {
		struct timeval tv;
		(void) gettimeofday(&tv,NULL);
		if((Last_CN_HTime - (long) tv.tv_sec))
		  {
		    Last_CN_HTime = (long) tv.tv_sec;
		    EZ_WidgetWidth(widget) = w;
		    EZ_WidgetHeight(widget) = h;
		    EZ_WidgetWidthHint(widget) = w;
		    EZ_WidgetHeightHint(widget) = h;
		    EZ_ClearWidgetSizeComputedFlag(widget);
		    if(EZ_WidgetMapped(widget)) EZ_ReDisplayWidget(widget); 
		  }
	      }
	    else if( x != 0 && y != 0 && (x != EZ_WidgetOriginX(widget) ||
					  y != EZ_WidgetOriginY(widget)))
	      {
		EZ_SetWMSizeHints(widget, 0);  /* been moved */
		if(EZ_GetWidgetTransientFlag(widget) == 0)
		  EZ_GenerateEmbedingRequest(widget, x,y, w,h);
	      }
	  }
      
	if(EZ_WidgetType(widget) != EZ_WIDGET_3D_CANVAS)
	  return;
      }
      break;
    case LeaveNotify:
      break;
    case EnterNotify:
      if(event->xcrossing.focus) 
	{
	  if(widget != EZ_CurrentFocusingWidget) EZ_ResetFocus(widget, 1);
	}
      break;
    case FocusIn:
      if(EZ_WidgetParent(widget) == NULL && !event->xfocus.send_event )
	{
	  if(EZ_CurrentFocusingWidget != widget &&
	     EZ_LookupWidgetFromAllHT(EZ_PendingFocus) == NULL)
	    {
	      EZ_PendingFocus = NULL;
	      EZ_ResetFocus(widget, 0);
	    }
	}
      if(widget == EZ_PendingFocus) EZ_PendingFocus = NULL;
      if(EZ_CurrentFocusingWidget == widget && EZ_GetWidgetFocusableFlag(widget))
	{ EZ_WeAreInFocus = 1; EZ_DrawFocusIndicator(widget, None, FocusIn); }
      break;
    case FocusOut:
      if(EZ_WidgetPadB(widget) > 0 && !event->xfocus.send_event)
	{
	  if(EZ_CurrentFocusingWidget != widget)
	    EZ_DrawFocusIndicator(widget, None, FocusOut);
	  else /* check if we have the focus */
	    {
	      Window fw; int rt, out=0;
	      EZ_Widget *tmp;
	      XGetInputFocus(EZ_Display, &fw, &rt);
	      tmp = EZ_LookupWidgetFromMappedHT(fw);
	      if(tmp == NULL) out = 1;
	      else
		{
		  EZ_Widget *roota, *rootb;
		  roota = widget; while(EZ_WidgetParent(roota)) roota = EZ_WidgetParent(roota);
		  rootb = tmp;    while(EZ_WidgetParent(rootb)) rootb = EZ_WidgetParent(rootb); 
		  if(roota != rootb) out = 1;
		}
	      if(out) EZ_DrawFocusIndicator(widget, None, FocusOut);
	    }
	}
      if(EZ_CurrentFocusingWidget == widget)
	{ EZ_WeAreInFocus = 0; EZ_DrawFocusIndicator(widget, None, FocusOut); }
      break;
    case Expose:
      {
	/*
	 * dig out the last Expose event on win
	 */
	while(XCheckWindowEvent(EZ_DisplayForWidgets,win, ExposureMask, event));
      }
      break;
    default:
      break;
    }

  /*--------------------------------------------------------
   * Event occured inside a widget window. 
   *------------------------------------------------------*/
  {
    int i = EZ_WidgetType(widget);
    (EZ_WidgetHandlingFunctions[i]->EventHandle)(widget,event);
  }

  /*
   * if a ButtonPress is sent by us, wait a little bit.
   */
  if(event->type == ButtonPress && event->xbutton.send_event)
    {
     int type = EZ_WidgetType(widget);
     if(type != EZ_WIDGET_MENU_BUTTON && type != EZ_WIDGET_3D_CANVAS)
       {
	 XFlush(EZ_Display);
	 EZ_SitForALittleBit(300000);
       }
    }
}

/********************************************************************************
 * invoke user-defined event handler 
 */
void EZ_InvokePrivateEventHandler(event)
     XEvent *event;
{
  EZ_Widget *widget;
  Window win = event->xany.window;
  EZ_EventHandlerStruct *handlers;
  int type = event->type;
  /*
   * property Notify: used for inter-client communication
   */
  if(type == PropertyNotify && win == EZ_DummyWindow)
    {
      EZ_HandlePropertyNotify(event);
      return;
    }

  widget = EZ_LookupWidgetFromMappedHT(win);
   
  /*
   * client message: communication with the window manager
   */
  if(type == ClientMessage)
    {
      EZ_HandleClientMessage(widget, EZ_CLIENT_MESSAGE, event);
      return;
    }

  /* if event is not associated to a widget, invoke NotWidgetEventHandlers */
  if(widget == (EZ_Widget *)NULL)
    {
      handlers = EZ_NoWidgetEventHandlers;
      while(handlers)
	{
	  if(handlers->handler) (handlers->handler)(NULL,handlers->data, 0, event);
	  handlers = handlers->next;
	}
      return;
    }

  /* Button events and Crossing events hides the current bubble help */
  if(EZ_BubbleHelpOn)
    {
      switch(event->type)
	{
	case EnterNotify:  /* These two events reset LastBubbleWidget */
	  LastBubbleWindow = win;
	  EZ_HideBubbleHelp();   /* remove bubble */
	  EZ_GetCurrentTime( &LastBubbleTime); /* record current time */
	  break;
	case LeaveNotify:
	  LastBubbleWindow = (Window)NULL;
	  EZ_HideBubbleHelp();
	  break;
	  /* These two events block bubble help be displayed for widget */
	case ButtonPress:  
	case ButtonRelease:
	case KeyPress:
	  LastBubbleWindow = (Window)NULL;
	  EZ_HideBubbleHelp();
	  break;
	  break;
	default:
	  break;
	}
    }

  /* now invoke the private event handler */
  handlers = EZ_WidgetEventHandlers(widget);
  
  if(handlers != NULL)
    {
      if(EZ_GrabedWidget)
	{
	  /* if(EZ_WidgetIsViewable(EZ_GrabedWidget))*/
	  if(EZ_LookupWidgetFromAllHT(EZ_GrabedWidget) &&
	     EZ_WidgetWindow(EZ_GrabedWidget) != None)
	    {
	      switch(type)
		{
		case Expose:
		case ConfigureNotify:
		case EnterNotify:
		case LeaveNotify:
		case FocusIn:
		case FocusOut:
		case MotionNotify:
		  break;
		default:
		  return;
		  break;
		}
	    }
	  else EZ_GrabedWidget = NULL;
	}
      {
	int etype = EZ_XEvent2GLEvent(widget, event);
	EZ_WidgetBlocked(widget) += 1;
	while(handlers)
	  {
	    if(handlers->handler) (handlers->handler)(widget,handlers->data, etype, event);
	    handlers = handlers->next;
	  }
	EZ_WidgetBlocked(widget) -= 1;
      }
    }
}

/********************************************************************************/
void EZ_WarpPointer(xx, yy)
     int xx, yy;
{
  int          rx,ry,x,y;
  unsigned int mask;
  Window       root,child_win;
                          
  XQueryPointer(EZ_Display, RootWindow(EZ_Display,EZ_ScreenNum),
		&root,  &child_win,   &rx, &ry,  &x,&y,
		&mask);
  XWarpPointer(EZ_Display, RootWindow(EZ_Display,EZ_ScreenNum),
	       None,  rx, ry, 0, 0, xx, yy);
}
/********************************************************************************
 * 
 *  Handle widget window events
 */

void EZ_HandleWidgetWindowEvent(widget, event)
     EZ_Widget *widget;
     XEvent          *event;
{
  int  i;
  if(widget == (EZ_Widget *)NULL) return;
  
  i = EZ_WidgetType(widget);
  (EZ_WidgetHandlingFunctions[i]->EventHandle)(widget,event);
}

/********************************************************************************
 * 
 *   Set the global popup menu
 */
void  EZ_TiePopupMenu(widget, button, where)
     EZ_Widget *widget;       /* the popup  */
     int   button, where;     /* the button the popup is binded to */
{
  if(widget != (EZ_Widget *)NULL &&
     EZ_WidgetType(widget) == EZ_WIDGET_POPUP_MENU)
    {
      EZ_GlobalEvent *tt =  &EZ_GlobalKeyEvent[EZ_GLOBALEVENT_HASH_SIZE-1];
      tt->modifiers = button;
      tt->widget = widget;
      
      if(button == EZ_Btn1)
	PopupPosition[0] = where;
      else if(button == EZ_Btn2)
	PopupPosition[1] = where;
      else if(button == EZ_Btn3)
	PopupPosition[2] = where;
    }
}

/********************************************************************************
 * 
 *  Handle global key shortcut events. (Allowed widget Type: buttons )
 */
void  EZ_HandleGlobalKeyEvent(widget,x,y)
     EZ_Widget *widget; int x, y;
{
  XEvent       event;
  
  if(widget != (EZ_Widget *)NULL)
    {
      /*---------------------------------------
       *  Just send a button press-release pair
       *---------------------------------------*/
      event.xbutton.type = ButtonPress;
      event.xbutton.display = EZ_Display;
      event.xbutton.window = EZ_WidgetWindow(widget);
      event.xbutton.button = EZ_Btn1;
      event.xbutton.time  = CurrentTime;
      event.xbutton.x = x;
      event.xbutton.y = y;
      XSendEvent(EZ_Display, EZ_WidgetWindow(widget), False,
		 ButtonPressMask, &event);

      event.xbutton.type = ButtonRelease;
      XSendEvent(EZ_Display, EZ_WidgetWindow(widget), False,
		 ButtonReleaseMask, &event);	  
    }
}

static void  EZ_HandleGlobalKeyEventCarefully(widget,event, x, y)
     EZ_Widget *widget;
     XEvent *event; 
     int x, y;
{
  if(widget != (EZ_Widget *)NULL)
    {
      switch(EZ_WidgetType(widget))
	{
	  /* cannot send a button press event, need a double press */
	case EZ_WIDGET_ILIST_BOX: 
	case EZ_WIDGET_IWORK_AREA:
	case EZ_WIDGET_ITREE:
	case EZ_WIDGET_IFANCY_LIST_BOX:
	  {
	    XSendEvent(EZ_Display, EZ_WidgetWindow(widget), False,
		       KeyPressMask, event);
	  }
	  break;
	  /* may be some more later */
	default:
	  EZ_HandleGlobalKeyEvent(widget, x, y);
	  break;
	}
    }
}

/**********************************************************************
 *
 *  Warp the pointer to a widget window. 
 *  if where = 0, warp the pointer near upper left corner.
 *  if where = 1, warp the pointer near the lower right corner.
 */
void EZ_WarpPointerToWidgetWindow(widget, where)
     EZ_Widget *widget;
     int       where;
{
  Window       junkwin, root, win;
  int          wx,wy, rx,ry,x,y;
  unsigned int mask;

  win = EZ_WidgetWindow(widget);
  if(where == 0) { x = 3; y = 3;}
  else if(where > 0)
    {
      x = EZ_WidgetWidth(widget) - 3;
      y = EZ_WidgetHeight(widget) - 3;
    }
  else
    {
      x = -3; y = -3;
    }

  XTranslateCoordinates(EZ_DisplayForWidgets, win,
			RootWindow(EZ_DisplayForWidgets, EZ_ScreenNumForWidgets),
			x,y, &wx,&wy, &junkwin); 
  XQueryPointer(EZ_DisplayForWidgets,
		RootWindow(EZ_DisplayForWidgets, EZ_ScreenNumForWidgets),
		&root,  &junkwin,   &rx, &ry,  &x,&y,  &mask);

  XWarpPointer(EZ_DisplayForWidgets,
	       RootWindow(EZ_DisplayForWidgets,EZ_ScreenNumForWidgets),
	       None,  rx, ry, 0, 0, 
	       (wx -rx), (wy -ry));
}

/********************************************************************************
 ***                                                                          ***
 ***              Handling Globally Binded KeyPress Events                    ***
 ***                                                                          ***
 ********************************************************************************/

/*
 *
 *  Clear the event hash table.
 */
void EZ_InitializeEventHT()
{
  int i;
  for(i = 0; i < EZ_GLOBALEVENT_HASH_SIZE; i++)
    EZ_GlobalKeyEvent[i].next = (EZ_GlobalEvent *)NULL;
  
  /*-----------------------------------------
   * the last one is saved for popupmenu
   *----------------------------------------*/
  EZ_GlobalKeyEvent[i-1].widget = (EZ_Widget *)NULL;
  EZ_GlobalKeyEvent[i-1].modifiers = 0;      
}

/****************************************************
 *
 *  Insert a  global event into the event hash table
 */
void  EZ_InsertGlobalKeyPressEvent(widget, modifiers, key)
     EZ_Widget *widget;
     int             modifiers;
     char            *key;
{
  int           i, hash; 
  EZ_GlobalEvent *event, *prev; 

  hash = EZ_HASH_CHAR((*key));
  /*
   * first, remove any binded accelerator for this widget
   */
  for(i = 0; i < EZ_GLOBALEVENT_HASH_SIZE - 1; i++)
    {
      prev =  &EZ_GlobalKeyEvent[i];
      event = prev->next;
      while(event !=  (EZ_GlobalEvent *)NULL)
	{
	  if(event->widget == widget)
	    {
	      prev->next = event->next;
	      (void)my_free( (char *)event); 
	      break;
	    }
	  prev = event;
	  event= event->next;	    
	}
    }
  /*----------------------------------------
   * insert this event
   *---------------------------------------*/
  prev =  &EZ_GlobalKeyEvent[hash];
  event = (EZ_GlobalEvent *)my_malloc(sizeof(EZ_GlobalEvent),_KEY_EVENT_HT_ENTRY_);
  if(!event) EZ_OutOfMemory("EZ_InsertGlobalKeyPressEvent");
  event->widget = widget;
  event->modifiers = modifiers;
  event->next = prev->next;
  prev->next = event;
}

/******************************************************************
 *
 *  Given a modifier and char, search a event in
 *  the global KeyPress event hash table. x_r and y_r returns
 *  a point inside the window the widget resides.
 */
int EZ_GetGlobalKeyEvent(modifiers, key, root,widget_return, x_r, y_r)
     EZ_Widget *root, **widget_return;
     int             modifiers, *x_r, *y_r;
     char            *key;
{
  int              hash = EZ_HASH_CHAR( (*key));
  EZ_GlobalEvent   *head = &(EZ_GlobalKeyEvent[hash]);
  EZ_GlobalEvent   *event = head->next;
  EZ_Widget        *widget;
  
  while(event != (EZ_GlobalEvent *)NULL)
    {
      if(event->modifiers == modifiers)
	{
	  /*-------------------------------------------
	   * find a match, check if the widget window
	   * is mapped and it is a descendant of root.
	   *------------------------------------------*/
	  widget = event->widget;
	  if(EZ_LookupWidgetFromAllHT(widget) == widget)
	    {
	      if(EZ_WidgetMapped(widget) != 0 && EZ_GetWidgetDisabledFlag(widget) == 0)
		{
		  EZ_Widget *parent = EZ_WidgetParent(widget);
		  int ok = 1;
		  while(parent != (EZ_Widget *)NULL)
		    {
		      if(EZ_WidgetMapped(parent) == 0) {ok = 0; break;}
		      parent = EZ_WidgetParent(parent);
		    }
		  if(ok)
		    {
		      int xx = 2; int yy = 2;
		      EZ_Widget *tmp = widget;
		      while(EZ_WidgetType(tmp) == EZ_WIDGET_NW_LABEL)
			{
			  xx += EZ_WidgetOriginX(tmp);
			  yy += EZ_WidgetOriginY(tmp);
			  tmp = EZ_WidgetParent(tmp);
			}
		      *widget_return = tmp;
		      if(x_r) *x_r = xx;
		      if(y_r) *y_r = yy;
		      head->next = event->next;
		      event->next = NULL;
		      while(head->next != NULL) head = head->next;
		      head->next = event;
		      return(1);
		    }
		}
	       /* try next: widget is not viewable */
	      head = event; 
	      event = event->next;
	    }
	  else
	    {
	      head->next = event->next;
	      (void)my_free(event);
	      event = head->next;
	    }
	}
      else
	{
	  head = event; 
	  event = event->next;
	}
    }
  return(0);
}

/****************************************************
 *
 *  Remove the key shortcut for widget from the 
 *  global KeyEvent table.
 */
void EZ_RemoveGlobalKeyEvent(modifiers, key, widget)
     EZ_Widget *widget;
     int             modifiers;
     char            *key;
{
  int              hash = EZ_HASH_CHAR( (*key));
  EZ_GlobalEvent   *prev =  &EZ_GlobalKeyEvent[hash];
  EZ_GlobalEvent   *event = prev->next;
  
  while(event != (EZ_GlobalEvent *)NULL)
    {
      if(event->widget == widget)
	{
	  prev->next = event->next;
	  (void)my_free( (char *)event);
	  return;
	}
      prev = event;
      event= event->next;
    }
}

void EZ_RemoveAllKeyShortcutForWidget(widget)
     EZ_Widget *widget;     
{
  int              i;
  EZ_GlobalEvent   *prev, *event;


  if(widget != (EZ_Widget *)NULL)
    for(i = 0; i < EZ_GLOBALEVENT_HASH_SIZE - 1; i++)
      {
	prev =  &EZ_GlobalKeyEvent[i];
	event = prev->next;
	while(event !=  (EZ_GlobalEvent *)NULL)
	  {
	    if(event->widget == widget)
	      {
		prev->next = event->next;
		(void)my_free( (char *)event);		
		break;
	      }
	    prev = event;
	    event= event->next;	    
	  }
      }
}

void EZ_RemoveAllGlobalKeyEvents()
{
  int              i;
  EZ_GlobalEvent   *prev, *event;


  for(i = 0; i < EZ_GLOBALEVENT_HASH_SIZE - 1; i++)
    {
      prev =  &EZ_GlobalKeyEvent[i];
      event = prev->next;
      prev->next = (EZ_GlobalEvent *)NULL;
      
      while(event !=  (EZ_GlobalEvent *)NULL)
	{
	  prev = event;
	  event= event->next;	    
	  (void)my_free( (char *)prev);		
	}
    }
}
  
/***************************************************************************************/

static int EZ_HASH_CHAR(c)
     char c;
{
  if(isupper(c)) c = tolower(c); 
  return (((int) c) & 0x7F);
}

/***************************************************************************************/

int EZ_CheckTimerEvents() 
{
  struct timeval ctv,*tv;
  long ts, tus;
  
  tv = &ctv;
  EZ_GetCurrentTime(tv);
  ts = tv->tv_sec; tus = tv->tv_usec;
  
  /******* handle bubble help ************/
  if(EZ_BubbleHelpOn)
    {
      EZ_Widget *widget;
      long       delay;

      delay = (ts - LastBubbleTime.tv_sec) * 1000000 +
	(tus - LastBubbleTime.tv_usec);
    
      if(delay > 800000 &&
	 (widget = EZ_LookupWidgetFromMappedHT(LastBubbleWindow)) &&
	 EZ_WidgetBubbleString(widget))
	{
	  int             rx,ry,x,y;
	  unsigned int    mask;
	  Window          root,win;
   
	  XQueryPointer(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			&root,              /* root return win */
			&win,               /* child ret win   */
			&rx, &ry,           /* x, y in root    */
			&x,&y,              /* x, y in win     */
			&mask
			);
	  EZ_SetWidgetPosition(EZ_BubbleHelp,rx+2,ry+8);
	  EZ_SetWidgetLabelString(EZ_BubbleHelp,EZ_WidgetBubbleString(widget));
	  EZ_DisplayWidget(EZ_BubbleHelp);
	  LastBubbleWindow = (Window) NULL;
	}
    }
  /******* end of bubble help ************/
  return(EZ_CheckRealTimers(ts, tus));
}
/***************************************************************************************/
static void EZ_HideBubbleHelp()
{
  EZ_Widget *wdt = EZ_BubbleHelp;

  if(EZ_WidgetMapped(wdt) != 0)
    {
      XUnmapWindow(EZ_Display, EZ_WidgetWindow(wdt));
      EZ_WidgetMapped(wdt) = 0;
    }
}
/***************************************************************************************/
static void EZ_HandleClientMessage(widget, eventType, event)
     EZ_Widget *widget;
     int       eventType;
     XEvent    *event;
{
  XClientMessageEvent *ev = (XClientMessageEvent *)event;   
  Atom mtype = ev->message_type;
  
  if(mtype == EZ_WM_PROTOCOLS_ATOM)
    {
      Atom c = (ev->data.l)[0];
      if(c == EZ_WM_DELETE_WINDOW_ATOM)
	{
	  if(widget && EZ_WidgetType(widget) == EZ_WIDGET_POPUP_MENU &&
	     EZ_PopupIsTearOff(widget))
	    {
	      EZ_Widget *bar = EZ_PopupIsTearLink(widget);
	      if(EZ_LookupWidgetFromAllHT(bar) == bar &&
		 EZ_WidgetType(bar) == EZ_WIDGET_MENU_TEAR_OFF_BAR)
		{
		  EZ_Widget *menu = EZ_WidgetParent(bar);
		  EZ_PopupCompanion(menu) = (EZ_Widget *)NULL;
		  EZ_MenuTearOffBarMenu(bar) = (EZ_Widget *)NULL;
		}
	      EZ_DestroyWidget(widget);
	      return;
	    }
	}
    }
  if(EZ_ClientMessageHandler.handler) 
    {
      (EZ_ClientMessageHandler.handler)(widget, EZ_ClientMessageHandler.data, 
					eventType, event);
    }
  else 
    {
      if(mtype == EZ_WM_PROTOCOLS_ATOM)
	{
	  Atom c = (ev->data.l)[0];
	  if(c == EZ_WM_DELETE_WINDOW_ATOM)
	    {
	      EZ_Shutdown();
	      exit(EZ_EXIT_OK);
	    }
	}
    }
}
/***************************************************************************************/
void EZ_RemoveEvent(event)
     XEvent *event;
{
  if(event) event->type  = 0;
}
/***************************************************************************************/
void EZ_TFEvents()
{
  (void) EZ_CheckTimerEvents();
  (void) EZ_CheckAppInputs(0);
}
/***************************************************************************************/
#ifdef EZ_DEBUG
void EZ_DumpGEventTable()
{
  int i;
  EZ_GlobalEvent   *event;  
  for(i = 0; i <  EZ_GLOBALEVENT_HASH_SIZE; i++)
    {
      event =  EZ_GlobalKeyEvent[i].next;
      while(event != (EZ_GlobalEvent *)NULL)
	{
	  printf("%c===> %d  %d  %lx\n",(char)i, event->modifiers,
		 EZ_WidgetType(event->widget),(unsigned long)event->widget);
	  event = event->next;
	}
    }
}
#endif

#undef _EZ_WIDGET_EVENT_C_
