

#include "WINGsP.h"

#include <X11/Xresource.h>



/* the notifications about views */

char *WMViewSizeDidChangeNotification = "WMViewSizeDidChangeNotification";
char *WMViewFocusDidChangeNotification = "WMViewFocusDidChangeNotification";
char *WMViewRealizedNotification = "WMViewRealizedNotification";


#define EVENT_MASK  \
	KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask| \
	EnterWindowMask|LeaveWindowMask|PointerMotionMask|ExposureMask| \
	VisibilityChangeMask|FocusChangeMask|PropertyChangeMask|\
	SubstructureNotifyMask|SubstructureRedirectMask


static XSetWindowAttributes defAtts= {
    None,                       /* background_pixmap */
    0,                          /* background_pixel */
    CopyFromParent,             /* border_pixmap */
    0,                          /* border_pixel */
    ForgetGravity,              /* bit_gravity */
    ForgetGravity,		/* win_gravity */
    NotUseful,                  /* backing_store */
    (unsigned) ~0,              /* backing_planes */
    0,                          /* backing_pixel */
    False,                      /* save_under */
    EVENT_MASK,   	        /* event_mask */
    0,                          /* do_not_propagate_mask */
    False,                      /* override_redirect */
    None,			/* colormap */
    None                        /* cursor */
};



static XContext ViewContext=0;	       /* context for views */




W_View*
W_GetViewForXWindow(Display *display, Window window)
{
    W_View *view;
    
    if (XFindContext(display, window, ViewContext, (XPointer*)&view)==0) {
	return view;
    }
    return NULL;
}



static void
unparentView(W_View *view)
{
    /* remove from parent's children list */
    if (view->parent!=NULL) {
	W_View *ptr;
	
	ptr = view->parent->childrenList;
	if (ptr == view) {
	    view->parent->childrenList = view->nextSister;
	} else {
	    while (ptr!=NULL) {
		if (ptr->nextSister == view) {
		    ptr->nextSister = view->nextSister;
		    break;
		} 
		ptr = ptr->nextSister;
	    }
	}
    }
    view->parent = NULL;
}


static void
adoptChildView(W_View *view, W_View *child)
{
    child->nextSister = NULL;

    /* add to end of children list of parent */
    if (view->childrenList == NULL) {
	view->childrenList = child;
    } else {
	W_View *ptr;
	
	ptr = view->childrenList;
	while (ptr->nextSister!=NULL) 
	    ptr = ptr->nextSister;
	ptr->nextSister = child;
    }
    child->parent = view;
}



static void
handleEvents(XEvent *event, void *data)
{
    W_View *view = (W_View*)data;
    
    if (event->type == ConfigureNotify) {
	
	if (event->xconfigure.width != view->size.width
	    || event->xconfigure.height != view->size.height) {
	    
	    if (view->flags.notifySizeChanged) {
		view->size.width = event->xconfigure.width;
		view->size.height = event->xconfigure.height;
		WMPostNotificationName(WMViewSizeDidChangeNotification,
				       view, NULL);
	    }
	}
    }
}


static W_View*
createView(W_Screen *screen, W_View *parent)
{
    W_View *view;

    if (ViewContext==0)
	ViewContext = XUniqueContext();
    
    view = wmalloc(sizeof(W_View));
    memset(view, 0, sizeof(W_View));
    
    view->refCount = 1;
    
    view->screen = screen;

    if (parent!=NULL) {
	/* attributes are not valid for root window */
	view->attribFlags = CWEventMask|CWBitGravity;
	view->attribs = defAtts;

	view->attribFlags |= CWBackPixel|CWColormap|CWBorderPixel;
	view->attribs.background_pixel = W_PIXEL(screen->gray);
	view->attribs.border_pixel = W_PIXEL(screen->black);
	view->attribs.colormap = screen->colormap;

	adoptChildView(parent, view);
    }

    return view;
}



W_View*
W_CreateView(W_View *parent)
{
    return createView(parent->screen, parent);
}


W_View*
W_CreateRootView(W_Screen *screen)
{
    W_View *view;
    
    view = createView(screen, NULL);

    view->window = screen->rootWin;
    
    view->flags.realized = 1;
    view->flags.mapped = 1;
    view->flags.root = 1;
    
    view->size.width = 
	WidthOfScreen(ScreenOfDisplay(screen->display, screen->screen));
    view->size.height =
	HeightOfScreen(ScreenOfDisplay(screen->display, screen->screen));

    return view;
}


W_View*
W_CreateTopView(W_Screen *screen)
{
    W_View *view;
    
    view = createView(screen, screen->rootView);
    if (!view)
	return NULL;

    view->flags.topLevel = 1;
    view->attribs.event_mask |= StructureNotifyMask;

    /* catch changes in the toplevel window (resize from user etc.) */
    WMCreateEventHandler(view, StructureNotifyMask, handleEvents, view);

    return view;
}



void
W_RealizeView(W_View *view)
{
    Window parentWID;
    Display *dpy = view->screen->display;
    W_View *ptr;

    assert(view->size.width > 0);
    assert(view->size.height > 0);

    
    if (view->parent && !view->parent->flags.realized) {
	wwarning("trying to realize widget of unrealized parent");
	return;
    }

    if (!view->flags.realized) {
	parentWID = view->parent->window;
	view->window = XCreateWindow(dpy, parentWID, view->pos.x, view->pos.y,
				     view->size.width, view->size.height, 0, 
				     view->screen->depth, InputOutput,
				     view->screen->visual, view->attribFlags,
				     &view->attribs);
    
	XSaveContext(dpy, view->window, ViewContext, (XPointer)view);

	view->flags.realized = 1;

	if (view->flags.mapWhenRealized) {
	    W_MapView(view);
	    view->flags.mapWhenRealized = 0;
	}

	WMPostNotificationName(WMViewRealizedNotification, view, NULL);
    }
    
    /* realize children */
    ptr = view->childrenList;
    while (ptr!=NULL) {
	W_RealizeView(ptr);

	ptr = ptr->nextSister;
    }
}



Bool
W_CheckInternalMessage(W_Screen *scr, XClientMessageEvent *cev, int event)
{
    if (cev->message_type == scr->internalMessage
	&& cev->format == 32 && cev->data.l[1] == event)
	return True;
    else
	return False;
}


void
W_ReparentView(W_View *view, W_View *newParent)
{
    int wasMapped;
    Display *dpy = view->screen->display;
    
    assert(!view->flags.topLevel);
    
    wasMapped = view->flags.mapped;
    if (wasMapped)
	W_UnmapView(view);

    unparentView(view);
    adoptChildView(newParent, view);

    if (view->flags.realized) {
	if (newParent->flags.realized) {
	    XReparentWindow(dpy, view->window, newParent->window, 0, 0);
	} else {
	    wwarning("trying to reparent realized view to unrealized parent");
	    return;
	}
    }
  
    view->pos.x = 0;
    view->pos.y = 0;
    
    if (wasMapped)
	W_MapView(view);
}



void
W_MapView(W_View *view)
{
    if (!view->flags.mapped) {
	if (view->flags.realized) {
	    XMapRaised(view->screen->display, view->window);
	    XFlush(view->screen->display);
	    view->flags.mapped = 1;
	} else {
	    view->flags.mapWhenRealized = 1;
	}
    }
}


/*
 * W_MapSubviews-
 *     maps all children of the current view that where already realized.
 */
void
W_MapSubviews(W_View *view)
{
    XMapSubwindows(view->screen->display, view->window);
    XFlush(view->screen->display);
    
    view = view->childrenList;
    while (view) {
	view->flags.mapped = 1;
	view->flags.mapWhenRealized = 0;
	view = view->nextSister;
    }
}



void
W_UnmapSubviews(W_View *view)
{
    XUnmapSubwindows(view->screen->display, view->window);
    XFlush(view->screen->display);
    
    view = view->childrenList;
    while (view) {
	view->flags.mapped = 0;
	view->flags.mapWhenRealized = 0;
	view = view->nextSister;
    }
}



void
W_UnmapView(W_View *view)
{
    view->flags.mapWhenRealized = 0;
    if (!view->flags.mapped)
	return;
  
    XUnmapWindow(view->screen->display, view->window);
    XFlush(view->screen->display);
    
    view->flags.mapped = 0;
}


W_View*
W_TopLevelOfView(W_View *view)
{
    W_View *toplevel;
    
    for (toplevel=view; !toplevel->flags.topLevel; toplevel=toplevel->parent);
    
    return toplevel;
}


static void
destroyView(W_View *view)
{
    W_View *ptr;

    if (view->flags.alreadyDead)
	return;
    view->flags.alreadyDead = 1;    

    /* Do not leave focus in a inexisting control */
    if (W_FocusedViewOfToplevel(W_TopLevelOfView(view))==view)
	W_SetFocusOfTopLevel(W_TopLevelOfView(view), NULL);
    
    if (view->flags.topLevel) {
	W_FocusInfo *info = view->screen->focusInfo;
	/* remove focus information associated to this toplevel */
	
	if (info) {
	    if (info->toplevel==view) {
		view->screen->focusInfo = info->next;
		free(info);
	    } else {
		while (info->next) {
		    if (info->next->toplevel == view)
			break;
		    info = info->next;
		}
		if (info->next) {
		    W_FocusInfo *next = info->next->next;
		    free(info->next);
		    info->next = next;
		} 
		/* else the toplevel did not have any focused subview */
	    }
	}
    }
    
    /* destroy children recursively */
    while (view->childrenList!=NULL) {
	ptr = view->childrenList;
	ptr->flags.parentDying = 1;

	W_DestroyView(ptr);

	if (ptr == view->childrenList) {
	    view->childrenList = ptr->nextSister;
	    ptr->parent = NULL;
	}
    }

    W_CallDestroyHandlers(view);

    if (view->flags.realized) {
	XDeleteContext(view->screen->display, view->window, ViewContext);
    
	/* if parent is being destroyed, it will die naturaly */
	if (!view->flags.parentDying || view->flags.topLevel)
	    XDestroyWindow(view->screen->display, view->window);
    }
    
    /* remove self from parent's children list */
    unparentView(view);

    W_CleanUpEvents(view);
#if 0    
    if (view->dragSourceProcs)
	free(view->dragSourceProcs);

    if (view->dragDestinationProcs)
	free(view->dragDestinationProcs);
#endif
    free(view);
}



void
W_DestroyView(W_View *view)
{
    W_ReleaseView(view);
}



void
W_MoveView(W_View *view, int x, int y)
{
    assert(view->flags.root==0);

    if (view->pos.x == x && view->pos.y == y) 
	return;
    
    if (view->flags.realized) {
	XMoveWindow(view->screen->display, view->window, x, y);
    }
    view->pos.x = x;
    view->pos.y = y;
}


void
W_ResizeView(W_View *view, unsigned int width, unsigned int height)
{    
    int shrinked;
    
    assert(width > 0);
    assert(height > 0);
    
    if (view->size.width == width && view->size.height == height)
	return;
    
    shrinked = width < view->size.width || height < view->size.height;
    
    if (view->flags.realized) {
	XResizeWindow(view->screen->display, view->window, width, height);
    } 
    view->size.width = width;
    view->size.height = height;
    
    if (view->flags.notifySizeChanged)
	WMPostNotificationName(WMViewSizeDidChangeNotification, view, NULL);
}


void
W_RedisplayView(W_View *view)
{
    XExposeEvent ev;
    
    if (!view->flags.mapped)
	return;
    
    ev.type = Expose;
    ev.display = view->screen->display;
    ev.window = view->window;
    ev.count = 0;

    WMHandleEvent((XEvent*)&ev);
}


void
W_SetViewBackgroundColor(W_View *view, WMColor *color)
{
    view->attribFlags |= CWBackPixel;
    view->attribs.background_pixel = color->color.pixel;
    if (view->flags.realized) {
	XSetWindowBackground(view->screen->display, view->window, 
			     color->color.pixel);
	XClearWindow(view->screen->display, view->window);
    }
}



W_View*
W_FocusedViewOfToplevel(W_View *view)
{
    WMScreen *scr = view->screen;
    W_FocusInfo *info;
    
    for (info = scr->focusInfo; info!=NULL; info = info->next)
	if (view == info->toplevel)
	    break;
    
    if (!info)
	return NULL;
    
    return info->focused;
}


void
W_SetFocusOfTopLevel(W_View *toplevel, W_View *view)
{
    WMScreen *scr = toplevel->screen;
    XEvent event;
    W_FocusInfo *info;
    
    for (info = scr->focusInfo; info!=NULL; info = info->next)
	if (toplevel == info->toplevel)
	    break;

    if (!info) {
	info = wmalloc(sizeof(W_FocusInfo));
	info->toplevel = toplevel;
	info->focused = view;
	info->next = scr->focusInfo;
	scr->focusInfo = info;
    } else {
	event.xfocus.mode = NotifyNormal;
	event.xfocus.detail = NotifyDetailNone;
	if (info->focused) {
	    /* simulate FocusOut event */
	    event.xfocus.type = FocusOut;
	    W_DispatchMessage(info->focused, &event);
	}
	info->focused = view;
    }
    if (view) {
	/* simulate FocusIn event */
	event.xfocus.type = FocusIn;
	W_DispatchMessage(view, &event);
    }
}


void 
W_BroadcastMessage(W_View *targetParent, XEvent *event)
{
    W_View *target;
    
    target = targetParent->childrenList;
    while (target!=NULL) {
	W_DispatchMessage(target, event);
	
	target = target->nextSister;
    }
}


void 
W_DispatchMessage(W_View *target, XEvent *event)
{
    if (target->window==None)
	return;
    event->xclient.window = target->window;
    event->xclient.display = target->screen->display;
    
    WMHandleEvent(event);
    /*
    XSendEvent(target->screen->display, target->window, False,
	       SubstructureNotifyMask, event);
     */
}



WMView*
W_RetainView(WMView *view)
{
    view->refCount++;
    return view;
}



void
W_ReleaseView(WMView *view)
{
    view->refCount--;
    if (view->refCount < 1) {
	destroyView(view);
    }
}


WMWidget*
WMWidgetOfView(WMView *view)
{
    return view->self;
}


WMSize 
WMGetViewSize(WMView *view)
{
    return view->size;
}

WMPoint 
WMGetViewPosition(WMView *view)
{
    return view->pos;
}


void
WMSetViewNotifySizeChanges(WMView *view, Bool flag)
{
    view->flags.notifySizeChanged = flag;
}

Window
WMViewXID(WMView *view)
{
    return view->window;
}

