

#include "WINGsP.h"

#include <X11/Xatom.h>


typedef struct W_Window {
    W_Class widgetClass;
    W_View *view;
    
    struct W_Window *nextPtr;	       /* next in the window list */
    
    char *caption;

    char *wname;
    
    WMSize minSize;
    WMSize maxSize;

    WMAction *closeAction;
    void *closeData;

    struct {
	unsigned int configured:1;
    } flags;
} _Window;


static void resizeWindow(WMWidget *, unsigned, unsigned);

struct W_ViewProcedureTable _WindowViewProcedures = {
    NULL,
	resizeWindow,
	NULL
};


#define DEFAULT_WIDTH	400
#define DEFAULT_HEIGHT	180
#define DEFAULT_TITLE	""


static void destroyWindow(_Window *win);

static void handleEvents();

static void realizeWindow();

static void 
realizeObserver(void *self, WMNotification *not)
{
    realizeWindow(self);
}


WMWindow*
WMCreateWindow(WMScreen *screen, char *name)
{
    _Window *win;
    static int initedApp = 0;

    win = wmalloc(sizeof(_Window));
    memset(win, 0, sizeof(_Window));

    win->widgetClass = WC_Window;

    win->view = W_CreateTopView(screen);
    if (!win->view) {
	free(win);
	return NULL;
    }
    win->view->self = win;

    win->wname = wstrdup(name);

    /* add to the window list of the screen (application) */
    win->nextPtr = screen->windowList;
    screen->windowList = win;

    WMCreateEventHandler(win->view, ExposureMask|StructureNotifyMask
			 |ClientMessageMask|FocusChangeMask, handleEvents, 
			 win);

    W_ResizeView(win->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);

    if (!initedApp) {
	W_InitApplication(screen);
	initedApp = 1;
    }
    
    WMAddNotificationObserver(realizeObserver, win, 
			      WMViewRealizedNotification, win->view);

    /* kluge. Find a better solution */
    W_SetFocusOfTopLevel(win->view, win->view);

    return win;
}


void
WMSetWindowTitle(WMWindow *win, char *title)
{
    if (win->caption!=NULL)
	free(win->caption);
    if (title!=NULL)
	win->caption = wstrdup(title);
    else
	win->caption = NULL;

    if (win->view->flags.realized) {
	XStoreName(win->view->screen->display, win->view->window, title);
    }
}




void
WMSetWindowCloseAction(WMWindow *win, WMAction *action, void *clientData)
{
    Atom *atoms;
    Atom *newAtoms;
    int count;
    WMScreen *scr = win->view->screen;

    if (win->view->flags.realized) {
	if (action && !win->closeAction) {
	    XGetWMProtocols(scr->display, win->view->window, &atoms, &count);
	    
	    newAtoms = wmalloc((count+1)*sizeof(Atom));
	    memcpy(newAtoms, atoms, count*sizeof(Atom));
	    newAtoms[count++] = scr->deleteWindowAtom;
	    XSetWMProtocols(scr->display, win->view->window, newAtoms, count+1);
	    XFree(atoms);
	    free(newAtoms);
	} else {
	    int i, ncount;
	    
	    XGetWMProtocols(scr->display, win->view->window, &atoms, &count);
	    
	    newAtoms = wmalloc((count-1)*sizeof(Atom));
	    ncount = 0;
	    for (i=0; i < count; i++) {
		if (atoms[i]==scr->deleteWindowAtom) {
		    newAtoms[i] = atoms[i];
		    ncount++;
		}
	    }
	    XSetWMProtocols(scr->display, win->view->window, newAtoms, ncount);
	    XFree(atoms);
	    free(newAtoms);
	}
    }
    win->closeAction = action;
    win->closeData = clientData;    
}


static void
resizeWindow(WMWidget *w, unsigned width, unsigned height)
{
    WMWindow *win = (WMWindow*)w;

    if (win->minSize.width > 0 && win->minSize.height > 0) {
	if (width < win->minSize.width)
	    width = win->minSize.width;
	if (height < win->minSize.height)
	    height = win->minSize.height;
    }
    
    if (win->maxSize.width > 0 && win->maxSize.height > 0) {
	if (width > win->maxSize.width)
	    width = win->maxSize.width;
	if (height > win->maxSize.height)
	    height = win->maxSize.height;
    }
    
    W_ResizeView(win->view, width, height);
}


static void
setSizeHints(WMWindow *win)
{
    XSizeHints *hints;
    
    hints = XAllocSizeHints();
    if (!hints) {
	wwarning("could not allocate memory for window size hints");
	return;
    }

    hints->flags = 0;
    if (win->minSize.width>0 && win->minSize.height>0) {
	hints->flags |= PMinSize;
	hints->min_width = win->minSize.width;
	hints->min_height = win->minSize.height;
    }
    if (win->maxSize.width>0 && win->maxSize.height>0) {
	hints->flags |= PMaxSize;
	hints->max_width = win->maxSize.width;
	hints->max_height = win->maxSize.height;
    }
    if (hints->flags) {
	XSetWMNormalHints(win->view->screen->display, win->view->window, hints);
    }
    XFree(hints);
}


static void 
realizeWindow(WMWindow *win)
{
    XWMHints *hints;
    XClassHint *classHint;
    WMScreen *scr = win->view->screen;
    Atom atoms[4];
    int count;
        
    classHint = XAllocClassHint();
    classHint->res_name = win->wname;
    classHint->res_class = WMGetApplicationName();
    XSetClassHint(scr->display, win->view->window, classHint);
    XFree(classHint);
    
    if (!scr->aflags.simpleApplication) {
	hints = XAllocWMHints();
	hints->flags = WindowGroupHint;
	hints->window_group = scr->groupLeader;
	XSetWMHints(scr->display, win->view->window, hints);
	XFree(hints);
    }
    
    count = 0;
    if (win->closeAction) {
	atoms[count++] = scr->deleteWindowAtom;
    }
    
    if (count>0)
	XSetWMProtocols(scr->display, win->view->window, atoms, count);
    
    if (win->caption)
	XStoreName(scr->display, win->view->window,  win->caption);
    
    setSizeHints(win);
}


void
WMHideWindow(WMWindow *win)
{
    WMUnmapWidget(win);
    XWithdrawWindow(win->view->screen->display, win->view->window,
		    win->view->screen->screen);
}


void
WMSetWindowMinSize(WMWindow *win, unsigned width, unsigned height)
{
    win->minSize.width = width;
    win->minSize.height = height;
    if (win->view->flags.realized)
	setSizeHints(win);
}



void
WMSetWindowMaxSize(WMWindow *win, unsigned width, unsigned height)
{
    win->maxSize.width = width;
    win->maxSize.height = height;
    if (win->view->flags.realized)
	setSizeHints(win);
}



static void
handleEvents(XEvent *event, void *clientData)
{
    _Window *win = (_Window*)clientData;
    
    
    switch (event->type) {
     case ClientMessage:
	if (event->xclient.message_type == win->view->screen->protocolsAtom
	    && event->xclient.format == 32 
	    && event->xclient.data.l[0]==win->view->screen->deleteWindowAtom) {
	    
	    if (win->closeAction) {
		(*win->closeAction)(win, win->closeData);
	    }
	}
	break;
     case DestroyNotify:
	destroyWindow(win);
	break;
    }
}




static void
destroyWindow(_Window *win)
{
    WMScreen *scr = win->view->screen;

    WMRemoveNotificationObserver(win);
    
    if (scr->windowList == win) {
	scr->windowList = scr->windowList->nextPtr;
    } else {
	WMWindow *ptr;
	ptr = scr->windowList;
	
	while (ptr->nextPtr) {
	    if (ptr->nextPtr==win) {
		ptr->nextPtr = ptr->nextPtr->nextPtr;
		break;
	    }
	    ptr = ptr->nextPtr;
	}
    }

    if (win->caption) {
	free(win->caption);
    }
    
    if (win->wname)
	free(win->wname);

    free(win);
}


