#ifndef CODE_XWINDOWS_CXX
#define CODE_XWINDOWS_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: Xwindows.cxx,v 1.9 2003/02/26 14:07:46 schmitzj Exp $)
 * 
 * Copyright (C) 1998 Jürgen Schmitz
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *-----------------------------------------------------------------------
 */

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "types.h"
#include <Xclasses/Xcolors.h>
#include <Xclasses/Xwindows.h>
#include <Xclasses/XCprogram.h>
#include <Xclasses/tools.h>
#include "include/Xwindows_p.h"
#include "../include/XCprogram_p.h"
#include "pixmap/default_icon.xpm"
#include <Xclasses/easy_dialog.h>

dlist allXwins;
static int xpm_anzahl=0;
static Pixmap icon,ishade;
static XpmAttributes xpmat;

Xwindows::Xwindows(void) : Xcolors()
{
	if (!(p=new pXwindows))
	{
		eprintf("Out of memory, can't allocate private memory\nTerminating.");
		exit(10);
	}
	p->o=this;
	Name("Xwindows");

	if (thisProgram)
	{
		ParentClass(thisProgram->name);
	}
}
pXwindows::pXwindows(void)
{
	flags=0;
	noWM=0;
	shadowmode=0;
	atpointer=0;
	sizehints.flags=0;
	wmhints.flags=0;
	win=0;
	keys=NULL;
	bargc=0;
	bargv=(char**)NULL;
	title="class Xwindows window";
	ititle="class Xwindows icon";
	cursor=0;
	keyactive=NULL;
	winnode.text=NULL;
}
pXwindows::~pXwindows(void)
{
	if (keys) delete keys;
	keys=NULL;
}
Xwindows::~Xwindows(void)
{
	Destroy();
	if (p) delete p;
}
void pXwindows::DefaultHints(void)
{
	if (++xpm_anzahl==1)
	{
		xpmat.colormap=cmap;
		xpmat.closeness=0x9999;
		xpmat.valuemask=XpmColormap|XpmCloseness;
		XpmCreatePixmapFromData(o->display(),win,default_icon,&icon,&ishade,&xpmat);
	}
	wmhints.icon_pixmap=icon;
	wmhints.icon_mask=ishade;
	wmhints.flags|=IconPixmapHint|IconMaskHint;
	if (cursor)
	{
		XFreeCursor(o->display(),cursor);
		cursor=0;
	}
	if (win && (cursor=XCreateFontCursor(o->display(),XC_top_left_arrow)))
	{
		XDefineCursor(o->display(),win,cursor);
	}
	if (o->Cmap()!=DefaultColormap(o->display(),o->screen()))
	{
		XSetWindowAttributes	xattr;
		xattr.colormap=o->Cmap();
		XChangeWindowAttributes(o->display(),win,CWColormap,&xattr);
	}
}
void pXwindows::FreeHints(void)
{
	if (cursor)
	{
		XFreeCursor(o->display(),cursor);
		cursor=0;
	}
	if (--xpm_anzahl==0)
	{
		XFreeColors(o->display(),cmap,xpmat.pixels,xpmat.npixels,0);
		XpmFreeAttributes(&xpmat);
		if (icon)   XFreePixmap(o->display(),icon);
		if (ishade) XFreePixmap(o->display(),ishade);
		icon=0;
	}
}
void pXwindows::ReadRes(int &x,int &y,int &width,int &height)
{
	char *w;
	if (!(flags&4))
	{
		if (w=o->GetResource("x"))
		{
			x=atoi(w);
			sizehints.flags|=USPosition;
			sizehints.x=abs(x);
		}
		if (w=o->GetResource("y"))
		{
			y=atoi(w);
			sizehints.flags|=USPosition;
			sizehints.y=abs(y);
		}
		if (w=o->GetResource("width"))
		{
			width=atoi(w);
			sizehints.flags|=USSize;
			sizehints.width=width;
		}
		if (w=o->GetResource("height"))
		{
			height=atoi(w);
			sizehints.flags|=USSize;
			sizehints.height=height;
		}

		if (w=o->GetResource("geometry"))
		{
			o->Geometry(w);
		}
		if (sizehints.flags&PMinSize)
		{
			if (width<sizehints.min_width)
			{
				width=sizehints.min_width;
			}
			if (height<sizehints.min_height)
			{
				height=sizehints.min_height;
			}
		}
		if (sizehints.flags&PMaxSize)
		{
			if (width>sizehints.max_width)
			{
				width=sizehints.max_width;
			}
			if (height>sizehints.max_height)
			{
				height=sizehints.max_height;
			}
		}
	}
	w=o->GetResource("iconify");
	if (w && !strcasecmp(w,"True")) o->OpenAsIcon();

	w=o->GetResource("atpointer");
	if (w && !strcasecmp(w,"True")) o->PlaceUnderPointer();
}

int Xwindows::Create(void)
{
	if (thisProgram && !ParentClass())
	{
		ParentClass(thisProgram->name);
	}
	if (!p->keys) p->keys=new keyclass(this);

	int x=0,y=0,w=10,h=10;
//	p->ReadRes(x,y,w,h);

	if (p->win=XCreateSimpleWindow(display(),
							DefaultRootWindow(display()),
							x,y, //p->sizehints.x,p->sizehints.y,
							w,h, // p->sizehints.width,p->sizehints.height,
							3,
							BlackPixel(display(),screen()),
							WhitePixel(display(),screen())))
	{
		p->DefaultHints();
		// Close-Gadget
		p->protocol[0]=XInternAtom(display(),"WM_DELETE_WINDOW",False);
		p->protocol[1]=XInternAtom(display(),"WM_SAVE_YOURSELF",False);
		XSetWMProtocols(display(),p->win,p->protocol,2);

		p->winnode.text=(char*)this;
		allXwins.AddTail(&p->winnode);

		return 1;
	}
	return 0;
}
int Xwindows::Create(int x,int y,int width,int height)
{
	if (thisProgram && !ParentClass())
	{
		ParentClass(thisProgram->name);
	}
	if (!p->keys) p->keys=new keyclass(this);

	p->ReadRes(x,y,width,height);

	if (p->flags&4)
	{
		x=p->sizehints.x;
		y=p->sizehints.y;
		width=p->sizehints.width;
		height=p->sizehints.height;
	}
	if (p->win=XCreateSimpleWindow(display(),
							DefaultRootWindow(display()),
							abs(x),abs(y),width,height,1,
							BlackPixel(display(),screen()),
							WhitePixel(display(),screen())))
	{							
		p->sizehints.x=abs(x);
		p->sizehints.y=abs(y);
		p->sizehints.width=width;
		p->sizehints.height=height;
		if (!(p->sizehints.flags & USPosition) && (x>=0) && (y>=0))
		{
			p->sizehints.flags|=PPosition;
		}
		if (!(p->sizehints.flags & USSize))
		{
			p->sizehints.flags|=PSize;
		}
		p->DefaultHints();

		// Close-Gadget
		p->protocol[0]=XInternAtom(display(),"WM_DELETE_WINDOW",False);
		p->protocol[1]=XInternAtom(display(),"WM_SAVE_YOURSELF",False);
		XSetWMProtocols(display(),p->win,p->protocol,2);

		p->winnode.text=(char*)this;
		allXwins.AddTail(&p->winnode);
		return 1;
	}
	return 0;
}
void Xwindows::MakeFit(void)
{
	int w,h,nw,nh,x,y,nx,ny,sw,sh;

	DisplaySize(display(),sw,sh);
	Position(x,y); nx=x; ny=y;
	Size(w,h); nw=w; nh=h;
	if (p->sizehints.flags&PMaxSize)
	{
		if (nw>p->sizehints.max_width)
		{
			nw=p->sizehints.max_width;
		}
		if (nh>p->sizehints.max_height)
		{
			nh=p->sizehints.max_height;
		}
	}
	if (x+nw>sw && !(p->sizehints.flags & USPosition))
	{
		nx=sw-nw;
		if (nx<2)
		{
			nw+=nx;
			nx=2;
		}
	}
	if (y+nh>sh && !(p->sizehints.flags & USPosition))
	{
		ny=sh-nh;
		if (ny<2)
		{
			nh+=ny;
			ny=2;
		}
	}
	if (p->sizehints.flags&PMinSize)
	{
		if (nw<p->sizehints.min_width)
		{
			nw=p->sizehints.min_width;
		}
		if (nh<p->sizehints.min_height)
		{
			nh=p->sizehints.min_height;
		}
	}
	if (x!=nx || y!=ny) Move(nx,ny);
	if (w!=nw || h!=nh) Resize(nw,nh);
}
void Xwindows::Background(unsigned long col)
{
	XSetWindowBackground(display(),p->win,col);
}
void Xwindows::Geometry(char *txt)
{
	int x,y,width,height,flag;
	char buf[100]="";

	if (p->win)
	{
		sprintf(buf,"%dx%d+%d+%d",p->sizehints.width,p->sizehints.height,p->sizehints.x,p->sizehints.y);
	}
	flag=XGeometry(display(),screen(),txt,buf,5,1,1,0,0,
			  	   &x,&y,&width,&height);
//	int grav;
//	flag=XWMGeometry(display(),screen(),txt,buf,5,&p->sizehints,
//			  	     &x,&y,&width,&height,&grav);

	if (flag & XValue) { p->sizehints.x=x; p->sizehints.flags&=~PPosition; p->sizehints.flags|=USPosition; }
	if (flag & YValue) { p->sizehints.y=y; p->sizehints.flags&=~PPosition; p->sizehints.flags|=USPosition; }
	if (flag & WidthValue) { p->sizehints.width=width; p->sizehints.flags&=~PSize; p->sizehints.flags|=USSize; }
	if (flag & HeightValue) { p->sizehints.height=height; p->sizehints.flags&=~PSize; p->sizehints.flags|=USSize; }
	p->flags|=4;
	if (p->win)
	{
		XMoveResizeWindow(display(),p->win,p->sizehints.x,p->sizehints.y,p->sizehints.width,p->sizehints.height);
	}
}
void Xwindows::Geometry(int &argc,char *argv[])
{
	int c=0,i=1;
	char *w=NULL,*d;

	if (!thisProgram)
	{
		class XCprogram *dummy=new XCprogram(argv[0],XCP_Done);
		dummy=dummy;
	}
	d=thisProgram->o->GetResource("iconify");
	if (d && strcasecmp(d,"True")) OpenAsIcon();
	w=thisProgram->o->GetResource("geometry");

	while (i<argc)
	{
		if (!strcasecmp(argv[i],"-geometry"))
		{
			c=i+1;
			if (c<argc)
			{
				w=argv[c];
				remarg(argc,argv,i);
				argc--;
			}
			remarg(argc,argv,i);
			argc--;
		}
		else if (!strcasecmp(argv[i],"-iconify"))
		{
			remarg(argc,argv,i);
			argc--;
			OpenAsIcon();
		}
		else
			i++;
	}

	if (w)
	{
		Geometry(w);
	}
	p->bargc=argc;
	p->bargv=argv;
}
void Xwindows::Title(char *wtitle)
{
	p->title=wtitle;
	if (p->win) XStoreName(display(),p->win,p->title);
}
void Xwindows::IconTitle(char *wtitle)
{
	p->ititle=wtitle;
	if (p->win) XSetIconName(display(),p->win,p->ititle);
}
void Xwindows::Class(char *rname,char *rclass)
{
	p->classhint.res_name =rname;
	p->classhint.res_class=rclass;
	XSetClassHint(display(),p->win,&p->classhint);
}
void Xwindows::OpenAsIcon(void)
{
	p->wmhints.initial_state=IconicState;
	p->wmhints.flags|=StateHint;
	p->flags&=~2;
}
void Xwindows::OpenAsWindow(void)
{
	p->wmhints.initial_state=NormalState;
	p->wmhints.flags|=StateHint;
	p->flags&=~2;
}
void Xwindows::MinSize(int width,int height)
{
	p->sizehints.min_width=width;
	p->sizehints.min_height=height;
	p->sizehints.flags|=PMinSize;
}
void Xwindows::MaxSize(int width,int height)
{
	p->sizehints.max_width=width;
	p->sizehints.max_height=height;
	p->sizehints.flags|=PMaxSize;
}
void Xwindows::ResizeInc(int width,int height)
{
	p->sizehints.width_inc=width;
	p->sizehints.height_inc=height;
	p->sizehints.flags|=PResizeInc;
}
void Xwindows::Aspect(int minx,int miny,int maxx,int maxy)
{
	p->sizehints.min_aspect.x=minx;
	p->sizehints.min_aspect.y=miny;
	p->sizehints.max_aspect.x=maxx;
	p->sizehints.max_aspect.y=maxy;
	p->sizehints.flags|=PAspect;
}
void Xwindows::BaseSize(int width,int height)
{
	p->sizehints.base_width=width;
	p->sizehints.base_height=height;
	p->sizehints.flags|=PBaseSize;
}
void Xwindows::Event(long mask)
{
	XSelectInput(display(),p->win,mask);
}
void Xwindows::OverrideRedirect(void)
{
	XSetWindowAttributes	xattr;

	xattr.override_redirect=True;
	XChangeWindowAttributes(display(),p->win,CWOverrideRedirect,&xattr);
	p->noWM=1;
}
void Xwindows::Group(class Xwindows &w)
{
	p->wmhints.window_group=w.p->win;
	p->wmhints.flags|=WindowGroupHint;
}
void Xwindows::Group(class Xwindows *w)
{
	p->wmhints.window_group=w->p->win;
	p->wmhints.flags|=WindowGroupHint;
}
void Xwindows::Group(Window w)
{
	p->wmhints.window_group=w;
	p->wmhints.flags|=WindowGroupHint;
}
void Xwindows::TransientFor(Window w)
{
	if (p->win)
	{
		XSetTransientForHint(display(),p->win,w);
	}
}
void Xwindows::TransientFor(class Xwindows *w)
{
	if (p->win && w->p->win)
	{
		XSetTransientForHint(display(),p->win,w->p->win);
		if (p->sizehints.x<10 && p->sizehints.y<10 && !(p->sizehints.flags & PPosition))
		{
			int x,y;
			w->Position(x,y);
			Move(x+2,y+2);
		}
	}
}
void Xwindows::TransientFor(class Xwindows &w)
{
	TransientFor(&w);
}
void Xwindows::BorderWidth(int w)
{
	XSetWindowBorderWidth(display(),p->win,w);
}
void Xwindows::SaveUnder(void)
{
	XSetWindowAttributes	xattr;

	xattr.save_under=True;
	XChangeWindowAttributes(display(),p->win,CWSaveUnder,&xattr);
}
void Xwindows::PlaceUnderPointer(void)
{
	p->atpointer=1;
}

void Xwindows::Resize(int width,int height)
{
	if (p->sizehints.flags&PMinSize)
	{
		if (width<p->sizehints.min_width)
		{
			width=p->sizehints.min_width;
		}
		if (height<p->sizehints.min_height)
		{
			height=p->sizehints.min_height;
		}
	}
	if (p->sizehints.flags&PMaxSize)
	{
		if (width>p->sizehints.max_width)
		{
			width=p->sizehints.max_width;
		}
		if (height>p->sizehints.max_height)
		{
			height=p->sizehints.max_height;
		}
	}
	p->sizehints.width=width;
	p->sizehints.height=height;
	XResizeWindow(display(),p->win,width,height);	
}
void Xwindows::Move(int x,int y)
{
	p->sizehints.x=x;
	p->sizehints.y=y;
	XMoveWindow(display(),p->win,x,y);
}
void Xwindows::Move(int x,int y,int width,int height)
{
	p->sizehints.x=x;
	p->sizehints.y=y;
	p->sizehints.width=width;
	p->sizehints.height=height;
	XMoveResizeWindow(display(),p->win,x,y,width,height);
}

void Xwindows::Position(int &x,int &y)
{
	GetPositionOnRoot(display(),p->win,x,y);

//  so nicht ON ROOT!!!
//	XWindowAttributes xwa;
//	XGetWindowAttributes(display(),p->win,&xwa);
//	x=xwa.x;
//	y=xwa.y;
}
void Xwindows::WMPosition(int &x,int &y)
{
	::WMPosition(display(),p->win,x,y);
}
void Xwindows::Size(int &x,int &y)
{
	XWindowAttributes xwa;
	XGetWindowAttributes(display(),p->win,&xwa);
	x=xwa.width;
	y=xwa.height;
}

void Xwindows::Update(void)
{
	XFlush(display());
}
void Xwindows::Open(void)
{
	if (thisProgram && !ParentClass())
	{
		ParentClass(thisProgram->name);
	}
	if (p->atpointer)
	{
		Window dummyw;
		int dummyi;
		unsigned int dummyu;
		int x,y,w,h,dw,dh;
		XQueryPointer(display(),p->win,&dummyw,&dummyw,&x,&y,&dummyi,&dummyi,&dummyu);
		Size(w,h);
		DisplaySize(display(),dw,dh);
		x-=w/2;
		if (x<0) x=0;
		if (x+w>dw) x=dw-w-10;
		y-=h/2;
		if (y<0) y=0;
		if (y+h>dh) y=dh-h-10;

		p->sizehints.flags&=~PPosition; p->sizehints.flags|=USPosition;
		p->sizehints.flags&=~PPosition; p->sizehints.flags|=USPosition;
		Move(x,y);
	}
	if (!(p->flags & 1))
	{
		XSetStandardProperties(display(),p->win,p->title,p->ititle,None,p->bargv,p->bargc,&p->sizehints);

		XTextProperty tprop;
		char *lst[1];
		lst[0]=xc_gethostname();
		XStringListToTextProperty(lst,1,&tprop);
		XSetWMClientMachine(display(),p->win,&tprop);
		XFree(tprop.value);
		p->flags|=1;
	}
	if (!(p->flags & 2))
	{
		if (!p->noWM) XSetWMHints(display(),p->win,&p->wmhints);
		p->flags|=2;
	}

//	XMapRaised(display(),p->win);
	XMapWindow(display(),p->win);
}
void Xwindows::OpenU(void)
{
	Open(); Update();
}
extern Window focuswindow;
void Xwindows::Close(void)
{
	if (p->win)
	{
		XEvent ev;

		XUnmapWindow(display(),p->win);
        while (XCheckWindowEvent(display(),p->win,0xffffffff,&ev));
		if (focuswindow==p->win) focuswindow=0;
	}
}
void Xwindows::CloseU(void)
{
	Close(); Update();
}
void Xwindows::Destroy(void)
{
	if (p->winnode.text)
	{
		allXwins.Remove(&p->winnode);
		p->winnode.text=NULL;
	}
	if (p->win)
	{
		XEvent ev;

		XUndefineCursor(display(),p->win);
		XDestroyWindow(display(),p->win);
#ifdef USE_XSYNC
		XSync(display(),0);
#endif
		p->FreeHints();
#ifdef USE_XSYNC
		XSync(display(),0);
#endif
        while (XCheckWindowEvent(display(),p->win,0xffffffff,&ev));
	}
	p->win=0; p->flags=0;
	if (p->keys) delete p->keys;
	p->keys=NULL;
}

void Xwindows::UpdateHints(void)
{
	XSetNormalHints(display(),p->win,&p->sizehints);
}

int Xwindows::IsCloseEvent(XEvent *myevent)
{
	if (myevent->type==ClientMessage)
	{
         if (myevent->xclient.data.l[0]==p->protocol[0])
         {
         	return 1;
         }
	}
	return 0;
}
int Xwindows::IsDestroyEvent(XEvent *myevent)
{
	if (myevent->type==ClientMessage)
	{
         if (myevent->xclient.data.l[0]==p->protocol[1])
         {
         	return 1;
         }
	}
	return 0;
}
void Xwindows::DestroyContinue(char *cmd)
{
	XSetCommand(display(),p->win,p->bargv,p->bargc);
}
Window Xwindows::window(void)
{
	return p->win;
}
int Xwindows::UserSized(void)
{
	return p->sizehints.flags & USSize;
}
keyclass *Xwindows::Keyclass(void)
{
	if (!p->keys) p->keys=new keyclass(this);
	return p->keys;
}
void Xwindows::MCursor(int c) 
{
    if (p->cursor)
    {
        XUndefineCursor(display(),p->win);
        XFreeCursor(display(),p->cursor);
        p->cursor=0;
    }
    if (c<0) c=XC_top_left_arrow;
    if (p->cursor=XCreateFontCursor(display(),c))
    {
        if (p->win)
        {
            XDefineCursor(display(),p->win,p->cursor);
            Update();
        }
    }
}

gadget *Xwindows::Keyactive(gadget *g)
{
	gadget *old=p->keyactive;
	if (g) p->keyactive=g;
	return old;
}

void Xwindows::Raise(void)
{
	XRaiseWindow(display(),p->win);
}
void Xwindows::Lower(void)
{
	XLowerWindow(display(),p->win);
}

void Xwindows::Iconify(void)
{
	if (p->win)
	{
		XWMHints wmh;
		wmh.initial_state=IconicState;
		wmh.flags=StateHint;
		XSetWMHints(display(),p->win,&wmh);
	}
	else
		OpenAsIcon();
}
void Xwindows::UnIconify(void)
{
	if (p->win)
	{
		XWMHints wmh;
		wmh.initial_state=NormalState;
		wmh.flags=StateHint;
		XSetWMHints(display(),p->win,&wmh);
	}
	else
		OpenAsWindow();
}
int Xwindows::IsIconified(void)
{
	int r=0;
	if (p->win)
	{
		XWindowAttributes xwa;
		XGetWindowAttributes(display(),p->win,&xwa);
		if (xwa.map_state!=IsViewable) r=1;
	}
	return r;
}
void Xwindows::SetIcon(Pixmap px,Pixmap shad)
{
	XWMHints wmh;

	wmh.icon_pixmap=px;
	wmh.icon_mask=shad;
	wmh.flags=IconPixmapHint|(shad?IconMaskHint:0);
	XSetWMHints(display(),p->win,&wmh);

	p->wmhints.icon_pixmap=px;
	p->wmhints.icon_mask=shad;
	p->wmhints.flags|=IconPixmapHint|IconMaskHint;
}
void Xwindows::ResetIcon(void)
{
	SetIcon(icon,ishade);
}

void Xwindows::Beep(int vol,int duration)
{
	XKeyboardControl xkc;

	if (duration>=1)
	{
		xkc.bell_duration=duration;
		XChangeKeyboardControl(display(),KBBellDuration,&xkc);
	}
	if (vol>=-100 && vol<=100) XBell(display(),vol);
	if (duration>=1)
	{
		xkc.bell_duration=-1;
		XChangeKeyboardControl(display(),KBBellDuration,&xkc);
	}
}

int Xwindows::Alert(char *text,char *buttons,...)
{
	easy_dialog ed;
	char *buf;
	va_list valist;

	buf=new char[10000];
	if (!buf) return -1;

	va_start(valist,buttons);
	vsprintf(buf, text, valist);
	va_end(valist);

	ed.Text(buf);
	ed.Options(buttons);
	delete [] buf;

	return ed.Start(this);
}

#endif
