

/*
 * This event handling stuff was based on Tk.
 */

#include "WINGsP.h"


/* table to map event types to event masks */
static unsigned long eventMasks[] = {
    0,
    0,
    KeyPressMask,                       /* KeyPress */
    KeyReleaseMask,                     /* KeyRelease */
    ButtonPressMask,                    /* ButtonPress */
    ButtonReleaseMask,                  /* ButtonRelease */
    PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
            |Button1MotionMask|Button2MotionMask|Button3MotionMask
            |Button4MotionMask|Button5MotionMask,
                                        /* MotionNotify */
    EnterWindowMask,                    /* EnterNotify */
    LeaveWindowMask,                    /* LeaveNotify */
    FocusChangeMask,                    /* FocusIn */
    FocusChangeMask,                    /* FocusOut */
    KeymapStateMask,                    /* KeymapNotify */
    ExposureMask,                       /* Expose */
    ExposureMask,                       /* GraphicsExpose */
    ExposureMask,                       /* NoExpose */
    VisibilityChangeMask,               /* VisibilityNotify */
    SubstructureNotifyMask,             /* CreateNotify */
    StructureNotifyMask,                /* DestroyNotify */
    StructureNotifyMask,                /* UnmapNotify */
    StructureNotifyMask,                /* MapNotify */
    SubstructureRedirectMask,           /* MapRequest */
    StructureNotifyMask,                /* ReparentNotify */
    StructureNotifyMask,                /* ConfigureNotify */
    SubstructureRedirectMask,           /* ConfigureRequest */
    StructureNotifyMask,                /* GravityNotify */
    ResizeRedirectMask,                 /* ResizeRequest */
    StructureNotifyMask,                /* CirculateNotify */
    SubstructureRedirectMask,           /* CirculateRequest */
    PropertyChangeMask,                 /* PropertyNotify */
    0,                                  /* SelectionClear */
    0,                                  /* SelectionRequest */
    0,                                  /* SelectionNotify */
    ColormapChangeMask,                 /* ColormapNotify */
    ClientMessageMask,		        /* ClientMessage */
    0,			                /* Mapping Notify */
};


/*
 * WMCreateEventHandler--
 * 	Create an event handler and put it in the event handler list for the
 * view. If the same callback and clientdata are already used in another
 * handler, the masks are swapped.
 * 
 */
void
WMCreateEventHandler(WMView *view, unsigned long mask,
		     WMEventProc *eventProc, void *clientData)
{
    W_EventHandler *handler;
    W_EventHandler *ptr = view->handlerList;
    unsigned long eventMask;
        
    if (ptr==NULL) {
	handler = wmalloc(sizeof(W_EventHandler));

	handler->nextHandler = NULL;
	
	view->handlerList = handler;
	
	eventMask = mask;
    } else {
	handler = NULL;
	eventMask = mask;
	while (ptr != NULL) {
	    if (ptr->clientData == clientData && ptr->proc == eventProc) {
		handler = ptr;
	    }	    
	    eventMask |= ptr->eventMask;
	    
	    ptr = ptr->nextHandler;
	}
	if (!handler) {
	    handler = wmalloc(sizeof(W_EventHandler));
	    handler->nextHandler = view->handlerList;
	    view->handlerList = handler;
	}	
    }

    /* select events for window */
    handler->eventMask = mask;
    handler->proc = eventProc;
    handler->clientData = clientData;
}


/*
 * WMDeleteEventHandler--
 * 	Delete event handler matching arguments from windows
 * event handler list.
 * 
 */
void
WMDeleteEventHandler(WMView *view, unsigned long mask,
		     WMEventProc *eventProc, void *clientData)
{
    W_EventHandler *handler, *ptr, *pptr;
    
    ptr = view->handlerList;
    
    handler = NULL;
    pptr = NULL;
    
    while (ptr!=NULL) {
	if (ptr->eventMask == mask && ptr->proc == eventProc 
	    && ptr->clientData == clientData) {
	    handler = ptr;
	    break;
	}
	pptr = ptr;
	ptr = ptr->nextHandler;
    }
    
    if (!handler)
	return;
    
    if (!pptr) {
	view->handlerList = handler->nextHandler;
    } else {
	pptr->nextHandler = handler->nextHandler;
    }
    free(handler);
}


void
W_CleanUpEvents(WMView *view)
{
    W_EventHandler *ptr, *nptr;
    
    ptr = view->handlerList;
        
    while (ptr!=NULL) {
	nptr = ptr->nextHandler;
	free(ptr);
	ptr = nptr;
    }
}



static Time
getEventTime(WMScreen *screen, XEvent *event)
{
    switch (event->type) {
        case ButtonPress:
        case ButtonRelease:
            return event->xbutton.time;
        case KeyPress:
        case KeyRelease:
            return event->xkey.time;
        case MotionNotify:
            return event->xmotion.time;
        case EnterNotify:
        case LeaveNotify:
            return event->xcrossing.time;
        case PropertyNotify:
            return event->xproperty.time;
    }
    return screen->lastEventTime;
}


void
W_CallDestroyHandlers(W_View *view)
{
    XEvent event;
    W_EventHandler *hPtr;
    
    event.type = DestroyNotify;
    event.xdestroywindow.window = view->window;
    event.xdestroywindow.event = view->window;
    
    hPtr = view->handlerList;
    while (hPtr!=NULL) {
	if (hPtr->eventMask & StructureNotifyMask) {
	    (*hPtr->proc)(&event, hPtr->clientData);
	}
	
	hPtr = hPtr->nextHandler;
    }
}


int
WMHandleEvent(XEvent *event)
{
    W_EventHandler *hPtr;
    W_View *view;
    unsigned long mask;
    Window window;

#ifndef WMAKER
    if (event->type == MappingNotify) {
	XRefreshKeyboardMapping(&event->xmapping);
	return True;
    }
#endif

    mask = eventMasks[event->xany.type];
    
    window = event->xany.window;

    /* diferentiate SubstructureNotify with StructureNotify */
    if (mask == StructureNotifyMask) {
	if (event->xmap.event != event->xmap.window) {
	    mask = SubstructureNotifyMask;
	    window = event->xmap.event;
	}
    }

    view = W_GetViewForXWindow(event->xany.display, window);
    if (!view)
	return False;

    view->screen->lastEventTime = getEventTime(view->screen, event);

    


    /* if it's a key event, redispatch it to the focused control */
    if (mask & (KeyPressMask|KeyReleaseMask)) {
	if (view->screen->focusedControl) {
	    view = view->screen->focusedControl;
	}
    }

    hPtr = view->handlerList;
			     
    while (hPtr!=NULL) {
	
	if ((hPtr->eventMask & mask)) {
	    (*hPtr->proc)(event, hPtr->clientData);
	}
	
	hPtr = hPtr->nextHandler;
    }
    
    /* pass the event to the top level window of the widget */
    if (view->parent!=NULL) {
	while (view->parent!=NULL)
	    view = view->parent;
	
	hPtr = view->handlerList;

	while (hPtr!=NULL) {
	
	    if (hPtr->eventMask & mask) {
		(*hPtr->proc)(event, hPtr->clientData);
	    }
	    hPtr = hPtr->nextHandler;
	}
    }
    
    return True;
}


