#ifndef CODE_KEY_CXX
#define CODE_KEY_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: key.cxx,v 1.2 1998/07/29 23:02:32 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 <stdarg.h>
#include <string.h>
#include "types.h"
#include "../include/extra_keysym.h"
#include <Xclasses/gadget.h>
#include <Xclasses/Xwindows.h>
#include <Xclasses/keyclass.h>
#include <Xclasses/dlist.h>
#include <Xclasses/tools.h>
#include "../include/callback.h"
#include <Xclasses/callable.h>
#include "include/key_p.h"

static class dlist allkeys;
extern class gadget *keyactive;
extern class Xwindows helpwin;

int KeyHandler(Xclasses *obj,XEvent *event,KeySym key,char *taste)
{
	struct node *n=allkeys.head;
	int r=DKH_NOTHING;

	if (obj)
	{
		Xwindows *w;
		if (obj->IsClassType("Xwindows"))
			w=(Xwindows*)obj;
		else
			w=(Xwindows*)obj->ParentClassType("Xwindows");

		if (w && w!=&helpwin)
		{
			while (n->next)
			{
				if (w==((pkeyclass*)n->text)->win)
				{
					r=((pkeyclass*)n->text)->Handle(event,key,taste);
					return r;
				}
				n=n->next;
			}
		}
		else if (n->next)
		{
			r=((pkeyclass*)n->text)->Handle(event,key,taste);
			return r;
		}
	}
	return DKH_NOTHING;
}

keyclass *GetKeyclass(Display *d,Window w)
{
	struct node *n=allkeys.head;
	while (n->next)
	{
		if (IsParentWindow(d,w,((pkeyclass*)n->text)->win->window()))
		{
			return ((pkeyclass*)n->text)->o;
		}
		n=n->next;
	}
	return NULL;
}
keyclass *GetKeyclass(Xclasses *obj)
{
	Xclasses *p=obj;
	while (p)
	{
		struct node *n=allkeys.head;
		while (n->next)
		{
			if (p==((pkeyclass*)n->text)->win)
			{
				return ((pkeyclass*)n->text)->o;
			}
			n=n->next;
		}
		p=p->ParentClass();
	}
	return NULL;
}

keyclass::keyclass(Xwindows *w) : Xclasses("keyclass")
{
	while (!(p=new pkeyclass))
	{
		outOfMemory("keyclass");
	}
	p->o=this;
	p->win=w;
	SendHookTo(XK_KP_Enter,KEY_SHIFT|KEY_CONTROL|KEY_ALT,(void (*)(XEvent *,KeySym,char *,int))((void (*)(void))writeObjectTree));
	ParentClass(w);
}
pkeyclass::pkeyclass(void)
{
	verkettung.text=(char*)this;
	allkeys.AddTail(&verkettung);
	for(int i=0;i<MAXKEYS;i++) mykeys[i].mode=K_EMPTY;
}
pkeyclass::~pkeyclass()
{
	allkeys.Remove(&verkettung);
}
keyclass::~keyclass()
{
	if (p) delete p;
}
void keyclass::operator=(keyclass *k)
{
	warn_printf("Program uses wrong way to copy keyclass for different windows.\n"
		    "Should be *x1=*x2, not *x1=x2.",this);
	operator=(*k);
}
void keyclass::operator=(keyclass &k)
{
	p->win=k.p->win;
}
int pkeyclass::Handle(XEvent *event,KeySym key,char *taste)
{
	XWindowAttributes xwa;
	int ok=0,warn=0;
	int r=DKH_NOTHING;
	int h=FindHash(key,GetQualOf(event));

	if (h!=-1)
	{
		if (mykeys[h].mode==K_CALLBACK || mykeys[h].mode==K_CALLBACKSELECT)
		{
			ok=1;
		}
		else
		{
			do
			{
				XGetWindowAttributes(event->xkey.display,mykeys[h].to->gg_win(),&xwa);
				if (xwa.map_state!=IsViewable)
				{
					int done=0;
					do
					{
						h++;
						if (h>=MAXKEYS)
						{
							h=0;
							warn++;
							if (warn>1) ok=2;
						}
						if (mykeys[h].mode!=K_EMPTY && mykeys[h].mode!=K_FREE && mykeys[h].key==key && mykeys[h].qual==GetQualOf(event))
						{
							done=1;
						}
					}
					while (mykeys[h].mode!=K_EMPTY && !done && !ok);
 					if (!done) ok=2;
				}
				else
				{
					ok=1;
				}
			}
			while (!ok);
		}
		if (ok==1)
		{
			switch (mykeys[h].mode)
			{
				case K_SELECT: 			// Auswahl
					// keins wenn richtiges aktiv und Return
					event->xkey.keycode=XKeysymToKeycode(event->xkey.display,XK_Return);
					event->xkey.window=mykeys[h].to->gg_win();
					XPutBackEvent(event->xkey.display,event);
// PRÜFEN!!!
//					if ((!keyactive || !IsParentWindow(event->xkey.display,keyactive->gg_win(),mykeys[h].to->gg_win())) && key!=XK_Return && key!=XK_KP_Enter)
					{
						mykeys[h].to->ActivateKey();
//						event->xkey.keycode=XKeysymToKeycode(event->xkey.display,XK_Return);
//						event->xkey.window=mykeys[h].to->gg_win();
//						XPutBackEvent(event->xkey.display,event);
						r=DKH_DEACTIVATED;
					}
					break;
				case K_SEND:				// Taste weiterleiten
					// keins wenn richtiges aktiv
					if (!keyactive || !IsParentWindow(event->xkey.display,keyactive->gg_win(),mykeys[h].to->gg_win()))
					{
						mykeys[h].to->ActivateKey();
						event->xkey.window=mykeys[h].to->gg_win();
						XPutBackEvent(event->xkey.display,event);
						r=DKH_DEACTIVATED;
					}
					break;
				case K_ACTIVATE: 			// Key aktivieren
					// keins wenn richtiges aktiv
					if (!keyactive || !IsParentWindow(event->xkey.display,keyactive->gg_win(),mykeys[h].to->gg_win()))
					{
						mykeys[h].to->ActivateKey();
						r=DKH_DEACTIVATED;
					}
					break;
				case K_CALLBACK:			// Callback
					mykeys[h].fcb.call(event,(void*)key,taste,(void*)mykeys[h].userdata);
// 					if (mykeys[h].hookclass)
// 					{
// 						((void (*)(void *,XEvent *,KeySym,char *,int))mykeys[h].hook)(mykeys[h].hookclass,event,key,taste,mykeys[h].userdata);
// 					}
// 					else
// 					{
// 						mykeys[h].hook(event,key,taste,mykeys[h].userdata);
// 					}
					r=DKH_INTERNAL;
					break;
				case K_CALLBACKSELECT:	// Callback mit Gadget-Rückgabe
					{
						int c=0;
						c=(int)mykeys[h].fcb.call(event,(void*)key,taste,(void*)mykeys[h].userdata);
// 						if (mykeys[h].hookclass)
// 						{
// 							c=((int (*)(void *,XEvent *,KeySym,char *,int))mykeys[h].hook)(mykeys[h].hookclass,event,key,taste,mykeys[h].userdata);
// 						}
// 						else
// 						{
// 							c=((int (*)(XEvent *,KeySym,char *,int))mykeys[h].hook)(event,key,taste,mykeys[h].userdata);
// 						}
						if (c)
						{
							// g Auswahl simulieren
							event->type=OwnJSEvent;
							event->xkey.x=(int)mykeys[h].to;
							XPutBackEvent(event->xkey.display,event);
						}
						r=DKH_INTERNAL;
					}
					break;
			}
		}
	}
	return r;
}
int pkeyclass::GetQualOf(XEvent *ev)
{
	int h=0;
	if (ev->xkey.state & ShiftMask) h|=KEY_SHIFT;
	if (ev->xkey.state & ControlMask) h|=KEY_CONTROL;
	if (ev->xkey.state & Mod1Mask) h|=KEY_ALT;
	return h;
}
int pkeyclass::FreeHashEntry(unsigned int xkey)
{
	int h=(xkey*2)%MAXKEYS;
	int warn=0;
	while (mykeys[h].mode!=K_EMPTY && mykeys[h].mode!=K_FREE)
	{
		h++;
		if (h>=MAXKEYS)
		{
			h=0; warn++;
			if (warn>1) return -1;
		}
	}
	return h;
}
int pkeyclass::FindHash(unsigned int xkey,unsigned int qual)
{
	int h=(xkey*2)%MAXKEYS;
	int warn=0;
// printf("FH: %x %d: %d/%d %d/%d\n",mykeys,h,mykeys[h].key,xkey,mykeys[h].qual,qual);
	while (mykeys[h].mode==K_EMPTY||mykeys[h].mode==K_FREE||mykeys[h].key!=xkey||mykeys[h].qual!=qual)
	{
		h++;
		if (h>=MAXKEYS)
		{
			h=0; warn++;
			if (warn>1) return -1;
		}
	}
	return h;
}
void keyclass::FreeKey(unsigned int xkey,unsigned int qual)
{
	int h=p->FindHash(xkey,qual);
	if (h>=0) p->mykeys[h].mode=K_FREE;

	if (xkey==XK_F11) FreeKey(Sun_XK_F36,qual);
	if (xkey==XK_F12) FreeKey(Sun_XK_F37,qual);
}
void keyclass::FreeKey(char *key,unsigned int qual)
{
	FreeKey(XStringToKeysym(key),qual);
}
void pkeyclass::SendTo(enum modes mode,unsigned int xkey,unsigned int qual,gadget *to,int userd,void (*hook)(XEvent *,KeySym,char *,int),void (callable::*chook)(XEvent *,KeySym,char *,int),callable *hclass)
{
	if (xkey==NoSymbol) return;
	int h=FreeHashEntry(xkey);
	if (h==-1)
	{
		warn_printf("No free space found for key binding in hash table.",o);
		return;
	}

	mykeys[h].mode=mode;
	mykeys[h].key=xkey;
	mykeys[h].qual=qual;
	mykeys[h].to=to;
	mykeys[h].userdata=userd;
	if (hook)
		mykeys[h].fcb.set((CBCAST) hook);
	else
		mykeys[h].fcb.set(hclass,(CALLFUNCTION) chook);

	if (xkey==XK_F11) SendTo(mode,Sun_XK_F36,qual,to,userd,hook,chook,hclass);
	if (xkey==XK_F12) SendTo(mode,Sun_XK_F37,qual,to,userd,hook,chook,hclass);
}
void pkeyclass::SendTo(enum modes mode,char *key,unsigned int qual,gadget *to,int userd,void (*hook)(XEvent *,KeySym,char *,int),void (callable::*chook)(XEvent *,KeySym,char *,int),callable *hclass)
{
	SendTo(mode,XStringToKeysym(key),qual,to,userd,hook,chook,hclass);
}

void keyclass::SendSelectTo(unsigned int xkey,unsigned int qual,gadget *g)
{
	p->SendTo(K_SELECT,xkey,qual,g);
}
void keyclass::SendSelectTo(char *key,unsigned int qual,gadget *g)
{
	p->SendTo(K_SELECT,key,qual,g);
}
void keyclass::SendKeyTo(unsigned int xkey,unsigned int qual,gadget *g)
{
	p->SendTo(K_SEND,xkey,qual,g);
}
void keyclass::SendKeyTo(char *key,unsigned int qual,gadget *g)
{
	p->SendTo(K_SEND,key,qual,g);
}
void keyclass::SendActivateTo(unsigned int xkey,unsigned int qual,gadget *g)
{
	p->SendTo(K_ACTIVATE,xkey,qual,g);
}
void keyclass::SendActivateTo(char *key,unsigned int qual,gadget *g)
{
	p->SendTo(K_ACTIVATE,key,qual,g);
}

void keyclass::SendHookTo(unsigned int xkey,unsigned int qual,void (*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACK,xkey,qual,NULL,userdata,h);
}
void keyclass::SendHookTo(char *key,unsigned int qual,void (*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACK,key,qual,NULL,userdata,h);
}
void keyclass::SendHookTo(unsigned int xkey,unsigned int qual,callable *hc,void (callable::*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACK,xkey,qual,NULL,userdata,NULL,h,hc);
}
void keyclass::SendHookTo(char *key,unsigned int qual,callable *hc,void (callable::*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACK,key,qual,NULL,userdata,NULL,h,hc);
}

void keyclass::SendHookSelectTo(unsigned int xkey,unsigned int qual,gadget *g,void (*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACKSELECT,xkey,qual,g,userdata,h);
}
void keyclass::SendHookSelectTo(unsigned int xkey,unsigned int qual,gadget &g,void (*h)(XEvent *,KeySym,char *,int),int userdata)
{
	SendHookSelectTo(xkey,qual,g,h,userdata);
}
void keyclass::SendHookSelectTo(char *key,unsigned int qual,gadget *g,void (*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACKSELECT,key,qual,g,userdata,h);
}
void keyclass::SendHookSelectTo(char *key,unsigned int qual,gadget &g,void (*h)(XEvent *,KeySym,char *,int),int userdata)
{
	SendHookSelectTo(key,qual,g,h,userdata);
}

void keyclass::SendHookSelectTo(unsigned int xkey,unsigned int qual,gadget *g,callable *hc,void (callable::*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACKSELECT,xkey,qual,g,userdata,NULL,h,hc);
}
void keyclass::SendHookSelectTo(unsigned int xkey,unsigned int qual,gadget &g,callable *hc,void (callable::*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACKSELECT,xkey,qual,&g,userdata,NULL,h,hc);
}
void keyclass::SendHookSelectTo(char *key,unsigned int qual,gadget *g,callable *hc,void (callable::*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACKSELECT,key,qual,g,userdata,NULL,h,hc);
}
void keyclass::SendHookSelectTo(char *key,unsigned int qual,gadget &g,callable *hc,void (callable::*h)(XEvent *,KeySym,char *,int),int userdata)
{
	p->SendTo(K_CALLBACKSELECT,key,qual,&g,userdata,NULL,h,hc);
}

#endif
