#ifndef CODE_GADGET_CXX
#define CODE_GADGET_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: gadget.cxx,v 1.26 2003/02/26 14:07:45 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 <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <time.h>

#include "types.h"
#include "../include/defaults.h"
#include "../include/callback.h"
#include <Xclasses/Xcolors.h>
#include <Xclasses/Ddraw.h>
#include <Xclasses/popup.h>
#include <Xclasses/gadget.h>
#include <Xclasses/Xwindows.h>
#include <Xclasses/message.h>
#include <Xclasses/tools.h>
#include "include/gadget_p.h"
#include <Xclasses/gfx_text.h>
#include <Xclasses/dlist.h>
#include <Xclasses/callable.h>

extern XIM		 xim;				// von Xdisplay
extern XIMStyle best_style;			// von Xdisplay

int					helpmode=0;
class gadget 		*keyactive=NULL; // offen wegen Keyhandler
static class gadget *oldkey=NULL;
static class gadget *tickactive=NULL;
static class gadget *contextactive=NULL;
static class gadget *lastgg=NULL;
static class gadget *currenthelp=NULL;
Window				onlywindow=0;	// von appwindow
static class gadget dummy;

static int focus=0;
static int keyboard_active=0;
Window focuswindow=0;
static fdlist fdlist;
static ticklist ticklist;

static int icWorking=1;

// Destructors
gadget::~gadget()
{
    if (win) Free();
    if (p) delete p;
}
pgadget::~pgadget()
{
}
// Constuctors
gadget::gadget() : Xcolors()
{
	while (!(p=new pgadget))
	{
		outOfMemory("gadget");
	}
	p->o=this;
	parent  =0;
    win     =0;
    gc		=0;
    state   =0;
    active  =0;
    locked	=0;

	width	=1;
	height	=1;

	setlimit	=0;
	minwidth	=1;
	minheight	=1;
	maxwidth	=0xffffff;
	maxheight	=0xffffff;
	before_x	=0;
	minxaspect	=0;
	minyaspect	=0;
	maxxaspect	=0;
	maxyaspect	=0;

	white=(unsigned long)-1;
	black=(unsigned long)-1;

	weight=10;
	specialflags=0;
	Name("gadget");
}
pgadget::pgadget() : callable(), fcb()
{
    id				=0;
    cursor  		=0;
    oldcursor		=-1;
    locked_bak		=0;
    blocked_bak		=0;
    blocked			=0;
    grabed			=0;
    sendto			=NULL;
	myg				=NULL;
    mode    		=0; //GM_BUTTON1;
    events  		=0;
    eventmask		=0;
    helptext		=NULL;
    gpopup			=NULL;
	msghandler		=NULL;
	keyhandlermode	=KHM_ACTIVATE;
	key_count		=0;
	xkey_count		=0;

	background	=(unsigned long)-1;
	foreground	=(unsigned long)-1;
	font		=NULL;
	fontnr		=FONT_GADGET;

	cutprop		=0;
	ic=0;

	for(int i=0;i<5;i++) buttonmap[i]=i+1;
}

// copy constructor - better don't use!
gadget::gadget(gadget &g)
{  
/*    id        =g.id;
    func      =g.func;
    cursor    =g.cursor;
    mode      =g.mode;
    button    =g.button; 
    keystate  =g.keystate; 
    background=g.background;
    foreground=g.foreground;
    white     =g.white;
    black     =g.black;
    x         =g.x;
    y         =g.y;
    width     =g.width;
    height    =g.height;
    win       =0;
    parent    =g.parent;
*/
}
// private Funktionen
void pgadget::AddGG(void)
{
    ggs *g;

    if (g=new ggs)
    {
        g->win=o->win;
        g->ggclasse=o;
        Add(g);
        myg=g;
    }
}
void pgadget::RemGG(void)
{
	ggs *g;
	if (g=myg)
	{
		Rem(g);
		myg=NULL;
	}
}
char *gadget::Class(void)
{
	return "gadget";
}

int pgadget::ButtonAllowed(unsigned int &b)
{
	b=buttonmap[b-1];
    switch (b)
    {
        case 1:
            if (mode & GM_BUTTON1) return 1; else return 0;
        case 2:
            if (mode & GM_BUTTON2) return 2; else return 0;     
        case 3:
            if (mode & GM_BUTTON3) return 3; else return 0;             
        case 4:
            if (mode & GM_BUTTON4) return 4; else return 0;     
        case 5:
            if (mode & GM_BUTTON5) return 5; else return 0;     
        default:
            return 0;
    }
}
int pgadget::SendKey(XEvent *myevent)
{
    int r=-1,a;
    class gadget *gsendto;

	if (blocked) return 0;

	gsendto=FindReceiver();

	o->active|=a=(GBActivate(myevent,GA_KEY,gsendto)?GA_KEY:0);
	if (a&GA_KEY)
	{
	    r=0;
    	o->ActivateKey();
    	if (GBSelected(myevent,GA_KEY,1,gsendto))
    	{
//        	o->active&=~GA_KEY; // jetzt in DeactivateKey()
//        	o->DeactivateKey(); // wir deactivieren nicht mehr!
        	// Auswahl
			r=1;
    	}        
	}
    return r;
}
class gadget *pgadget::FindReceiver(void)
{
	class pgadget *g=this;

	o->wasfor=NULL;
	while (g->sendto)
	{
		g->sendto->wasfor=g->o;
		g=g->sendto->p;
	}
	return g->o;
}
#define MAX_ACTIVE_STACK	20
static int		active_stack=0;
static gadget 	*ticka_stack[MAX_ACTIVE_STACK];
void pgadget::SetVars(void)
{
	class pgadget *g=this;	
	while (g->sendto)
	{
		g->sendto->state|=o->state;
		g->sendto->active|=o->active;
		g=g->sendto->p;
	}
	ticka_stack[active_stack++]=tickactive;
}
void pgadget::ClearVars(void)
{
	class pgadget *g=this;	
	tickactive=ticka_stack[--active_stack];
	while (g->sendto)
	{
		g->sendto->state&=~o->state;
		g->sendto->active&=(~o->active)|GA_DCLICK;
		g=g->sendto->p;
	}
}
int pgadget::CheckVars(void)
{
	class pgadget *g=this;	
	int a=0;
	if (o->active & GA_DCLICK)
	{
		a=GA_DCLICK;
		o->active&=~GA_DCLICK;
	}
	while (g->sendto)
	{
		if (g->sendto->active & GA_DCLICK)
		{
			a=GA_DCLICK;
			g->sendto->active&=~GA_DCLICK;
		}
		else
		{
			a=0;
		}
		g=g->sendto->p;
	}
	return a;
}
void pgadget::GBExpose(XEvent *event,gadget *sendto)
{
	SetVars();
	sendto->GExpose(event);
	ClearVars();
}
void pgadget::GBPress(XEvent *myevent,class gadget *gsendto,int reason)
{
	if (blocked) return;

	int k=0,b=0;
	if (myevent && 
	    (myevent->type==MotionNotify||
	     myevent->type==ButtonPress||
	     myevent->type==ButtonRelease))
	{
		k=keystate;
		b=button;
	}
	SetVars();
	if (gpopup && gpopup->PopupOpen())
	{
		gpopup->GActionPress(
					 (myevent?myevent->xbutton.x:0),
    	             (myevent?myevent->xbutton.y:0),
					 (myevent?myevent->xbutton.x_root:0),
    	             (myevent?myevent->xbutton.y_root:0),
        	         k,
            	     b,reason);
	}
	else
	{
		gsendto->GActionPress(
					 (myevent?myevent->xbutton.x:0),
    	             (myevent?myevent->xbutton.y:0),
					 (myevent?myevent->xbutton.x_root:0),
    	             (myevent?myevent->xbutton.y_root:0),
        	         k,
            	     b,reason);
	}
	ClearVars();
}
void pgadget::GBRelease(XEvent *myevent,class gadget *gsendto,int reason)
{
	if (blocked) return;

	int k=0,b=0;
	if (myevent && 
	    (myevent->type==MotionNotify||
	     myevent->type==ButtonPress||
	     myevent->type==ButtonRelease))
	{
		k=keystate;
		b=button;
	}
	SetVars();
	if (gpopup && gpopup->PopupOpen())
	{
		gpopup->GActionRelease(
					 (myevent?myevent->xbutton.x:0),
    	             (myevent?myevent->xbutton.y:0),
					 (myevent?myevent->xbutton.x_root:0),
    	             (myevent?myevent->xbutton.y_root:0),
        	         k,
            	     b,reason);
	}
	else
	{
		gsendto->GActionRelease(
					   (myevent?myevent->xbutton.x:0),
    	               (myevent?myevent->xbutton.y:0),
					   (myevent?myevent->xbutton.x_root:0),
    	               (myevent?myevent->xbutton.y_root:0),
        	           k,
            	       b,reason);
	}
	ClearVars();
}
int pgadget::GBActivate(XEvent *event,int why,gadget *sendto)
{
	if (blocked && why!=GA_TICK) return 0;

	int r;
	SetVars();
	if (gpopup && gpopup->PopupOpen())
	{
		r=gpopup->GActivate(event,why);
		ClearVars();
		if (r & GSR_MESSAGE && ((gadget*)gpopup)->p->msghandler)
		{
			((gadget*)gpopup)->p->msghandler->Msg(gpopup,MSG_CHANGE);
		}
	}
	else
	{
		r=sendto->GActivate(event,why);
		ClearVars();
		if (r & GSR_MESSAGE && sendto->p->msghandler)
		{
			sendto->p->msghandler->Msg(sendto,MSG_CHANGE);
		}
	}
	return r & GAR_ACTIVE;
}
int pgadget::GBSelected(XEvent *event,int why,int wasabove,gadget *sendto)
{
	if (blocked && why!=GA_TICK) return 0;

	int r;
	SetVars();
	if (gpopup && gpopup->PopupOpen())
	{
		r=gpopup->GSelected(event,why,wasabove);
		ClearVars();
		if (r & GSR_MESSAGE && ((gadget*)gpopup)->p->msghandler)
		{
			((gadget*)gpopup)->p->msghandler->Msg(gpopup,MSG_FINAL);
		}
	}
	else
	{
		r=sendto->GSelected(event,why,wasabove);
		ClearVars();
		if (r & GSR_MESSAGE && sendto->p->msghandler)
		{
			sendto->p->msghandler->Msg(sendto,MSG_FINAL);
		}
	}
	return r & GSR_SELECTION;
}
int pgadget::GBMove(XEvent *myevent,class gadget *sendto)
{
	if (blocked) return 0;

	int r;
	SetVars();
	if (gpopup && gpopup->PopupOpen())
	{
		r=gpopup->GMouseMove(
						  (myevent?myevent->xmotion.x:0),
    	           		  (myevent?myevent->xmotion.y:0),
						  (myevent?myevent->xmotion.x_root:0),
    	           		  (myevent?myevent->xmotion.y_root:0),
        	       		  keystate,
            	   		  button);
		ClearVars();
		if (r & GSR_MESSAGE && ((gadget*)gpopup)->p->msghandler)
		{
			((gadget*)gpopup)->p->msghandler->Msg(gpopup,MSG_CHANGE);
		}
	}
	else
	{
	    r=sendto->GMouseMove(
	    				  (myevent?myevent->xmotion.x:0),
    	           		  (myevent?myevent->xmotion.y:0),
						  (myevent?myevent->xmotion.x_root:0),
    	           		  (myevent?myevent->xmotion.y_root:0),
        	       		  keystate,
            	   		  button);
		ClearVars();
		if (r & GSR_MESSAGE && sendto->p->msghandler)
		{
			sendto->p->msghandler->Msg(sendto,MSG_CHANGE);
		}
	}
	return r & GSR_SELECTION;
}
int pgadget::GClipH(int why,char *data,int size,class gadget *sendto)
{
	int r;
	SetVars();
	r=sendto->GClipHandler(why,data,size);
	ClearVars();
	if (r & GSR_MESSAGE && sendto->p->msghandler)
	{
		sendto->p->msghandler->Msg(sendto,MSG_FINAL);
	}
	return r & 0;
}
int pgadget::GKHandler(XEvent *ev,KeySym k,char *c,int i)
{
	return o->GKeyHandler(ev,k,c);
}
void pgadget::Glock(void)
{
	if (locked_bak && !o->locked)
	{
		if (o==keyactive) o->DeactivateKey();
		o->locked=locked_bak;
		if (o->win)
		{
			class gadget *gsendto;

			gsendto=FindReceiver();
			GBExpose(NULL,gsendto);
		}
	}                                                
}
void pgadget::Gunlock(void)
{
	if (o->locked)
	{
		if (char *w=o->GetResource("locked"))
		{
			if (!strcasecmp(w,"True"))
				return;
		}
		o->locked=0;
		locked_bak=0;
		if (o->win)
		{
			class gadget *gsendto;

			gsendto=FindReceiver();
			GBExpose(NULL,gsendto);
		}
	}
}
void pgadget::Gblock(void)
{
	if (blocked_bak && !blocked)
	{
		if (o==keyactive)
		{
			Xwindows *xw=(Xwindows*)keyactive->ParentClassType("Xwindows");
			if (xw)
			{
				xw->Keyactive(keyactive);
			}
			o->DeactivateKey();
		}
		blocked=blocked_bak;

	    if (cursor)
	    {
			if (o->win) XUndefineCursor(o->display(),o->win);
       		XFreeCursor(o->display(),cursor);
	        cursor=0;
   		}

	    if (cursor=XCreateFontCursor(o->display(),XC_watch))
   		{
       		if (o->win)
        	{
   	        	XDefineCursor(o->display(),o->win,cursor);
        	}
	    }
	}
}
void pgadget::Gunblock(void)
{
	if (blocked)
	{
		blocked=0;
		blocked_bak=0;
		o->MCursor(oldcursor);

		if (focuswindow)
		{
			Xwindows *aw=GetXwindowsOf(o->display(),focuswindow);
			if (aw)
			{
				gadget *k=aw->Keyactive(NULL);
				if (k==o)
					k->ActivateKey();
			}
		}
	}
}
int pgadget::funccall(void)
{ // 0 - Event intern
	resetObjectCheck();

	fcb.call((void*)o,NULL);
	return 1;
}

void pgadget::GetPreferredGeometry(char *name,XRectangle *area)
{
	XVaNestedList list;

	list=XVaCreateNestedList(0,XNAreaNeeded,area,NULL);

	XSetICValues(ic,name,list,NULL);
	XGetICValues(ic,name,list,NULL);
	XFree(list);
}
void pgadget::SetGeometry(char *name,XRectangle *area)
{
	XVaNestedList list;

	list=XVaCreateNestedList(0,XNArea,area,NULL);
	XSetICValues(ic,name,list,NULL);
	XFree(list);
}

// Public functions
void gadget::Free(void)
{
    setlimit=0;
	if (currenthelp==this)
	{
		p->CloseHelp();
	}
	if (keyactive==this)
	{
		keyactive=NULL;
	}
	if (oldkey==this)
	{
		oldkey=NULL;
	}
	if (tickactive==this)
	{
		tickactive=NULL;
	}
	if (p->gpopup)
	{
		p->gpopup->Free();
		p->gpopup=NULL;
	}
    if (p->cursor)
    {
		if (win) XUndefineCursor(display(),win);
		XFreeCursor(display(),p->cursor);
		p->cursor=0;
    }
    if (win)
    {
//		if (XGetSelectionOwner(display(),XA_PRIMARY)==win)
//		{
//			XSetSelectionOwner(display(),XA_PRIMARY,None,time(NULL));
//		}
		if (p->ic) XDestroyIC(p->ic);
		p->ic=0;

		XEvent ev;
	    p->RemGG();
        XDestroyWindow(display(),win);
#ifdef USE_XSYNC
		XSync(display(),0);
#endif
        XFreeGC(display(),gc);
        while (XCheckWindowEvent(display(),win,0xffffffff,&ev));
        gc=0;
        win=0;
    }
//	um Untergadgets leicht mit dem Font des "Chefs" versehen zu können,
//  darf der Font nicht mehr freigegeben werden, sonst würde deren auch
//  freigegeben!
}
void gadget::Dimensions(Window gparent,int gx,int gy,int gwidth,int gheight)
{
    x=gx; y=gy;
    width=gwidth; 
    height=gheight;
    parent=gparent;
}
void gadget::MessageHandler(class message *m)
{
	p->msghandler=m;
}
int gadget::ID(void)
{
	return p->id;
}
void gadget::ID(int gid)
{
	p->id=gid;
}
void gadget::Callback(void gfunc(class gadget*,int))
{
	p->fcb.set((CBCAST) gfunc);
}
static int old_callway=0;
void gadget::Callback(void *c,void gfunc(class gadget*,int))
{
	p->fcb.set(c,(CBCAST) gfunc);
	if (old_callway<10)
	{
		old_callway++;
		warn_printf("This program used the old obsolete way with casted methods to set\nan object callback. Please contact the author!\n(only if he/she is not part of the XC development team)\n",this);
	}
}
void gadget::Callback(void *c,...)
{
	cbset(p->fcb,c);
	if (old_callway<10)
	{
		old_callway++;
		warn_printf("This program used the old obsolete way with VarArg to set\nan object callback. Please contact the author!\n(only if he/she is not part of the XC development team)\n",this);
	}
}
void gadget::Callback(callable *c,void (callable::*gfunc)(gadget *,int))
{
	p->fcb.set(c,(CALLFUNCTION) gfunc);
}

static int used_old_id=0;
void gadget::ID(void gfunc(class gadget*,int))
{// alte Funktion, historisch!
	Callback(gfunc);
	if (!used_old_id)
	{
		used_old_id=1;
		warn_printf("This program used the old obsolete ID() way to set\nan object callback. Please contact the author!\n(only if he/she is not part of the XC development team)\n",this);
	}
}
void gadget::ID(void *c,void gfunc(class gadget*,int))
{// alte Funktione, historisch!
	Callback(c,gfunc);
	if (!used_old_id)
	{
		used_old_id=1;
		warn_printf("This program used the old obsolete ID() way to set\nan object callback. Please contact the author!\n(only if he/she is not part of the XC development team)\n",this);
	}
}
void gadget::ID(void gfunc(class gadget*,int),int gid)
{// alte Funktione, historisch!
	ID(gid);
	ID(gfunc);
	if (!used_old_id)
	{
		used_old_id=1;
		warn_printf("This program used the old obsolete ID() way to set\nan object callback. Please contact the author!\n(only if he/she is not part of the XC development team)\n",this);
	}
}
void gadget::ID(void *c,void gfunc(class gadget*,int),int gid)
{// alte Funktione, historisch!
	ID(gid);
	ID(c,gfunc);
	if (!used_old_id)
	{
		used_old_id=1;
		warn_printf("This program used the old obsolete ID() way to set\nan object callback. Please contact the author!\n(only if he/she is not part of the XC development team)\n",this);
	}
}
void gadget::CallCallback(void)
{
	p->fcb.call((void*)this,NULL);
}

void gadget::Weight(int w)
{
	weight=w/10;
	if (weight<1) weight=1;
	if (weight>300) weight=300;
}
//#define FALLBACKFONT1	DEFAULTFONT
#define FALLBACKFONT1	"fixed"
#define FALLBACKFONT2	"-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
int gadget::Font(char *name)
{
	XFontStruct *f;
	
	if (!display())
	{
		error_printf("Illegal font set for no display.",this);
		return 0;
	}

	if (f=FindFont(name))
	{
		p->fontnr=0;
		p->font=f;
	}
	else
	{
		if (f=XLoadQueryFont(display(),name))
		{
			fontAdd(name,f);
			p->fontnr=0;
			p->font=f;
		}
		else
		{
			char buf[30+strlen(name)];
			sprintf(buf,"Can't open font %s",name);
			warn_printf(buf,this);
			if (f=FindFont(FALLBACKFONT1))
			{
				fontAdd(name,f);
				p->fontnr=0;
				p->font=f;
			}
			else
			{
				if (f=XLoadQueryFont(display(),FALLBACKFONT1))
				{
//					fontAdd(FALLBACKFONT1,f);
					fontAdd(name,f);
					p->fontnr=0;
					p->font=f;
				}
				else
				{
					char buf[30+strlen(FALLBACKFONT1)];
					sprintf(buf,"Can't open fallback font %s!",FALLBACKFONT1);
					warn_printf(buf,this);
					if (f=FindFont(FALLBACKFONT2))
					{
						p->fontnr=0;
						p->font=f;
					}
					else
					{
						if (f=XLoadQueryFont(display(),FALLBACKFONT2))
						{
//							fontAdd(FALLBACKFONT2,f);
							fontAdd(name,f);
							p->fontnr=0;
							p->font=f;
						}
						else
						{
							fatal_printf("Can't open any font!",this);
							p->fontnr=0;
							p->font=NULL;
						}
					}
				}
			}
		}
	}
   	if (p->font && gc) XSetFont(display(),gc,p->font->fid);
	return (int)f;
}
int gadget::Font(int f)
{
	p->fontnr=f;
	p->font=NULL;
	return 1;
}
XFontStruct	*gadget::gg_font(void)
{
	if (!p->font)
	{
		if (p->fontnr==0)
		{
			p->font=p->dfont();
		}
		else
		{
			int o=p->fontnr;
			if (Font(defaults.font[o]))
				p->fontnr=o;
		}
	}
	return p->font;
}
void gadget::gg_font(XFontStruct *f)
{
	p->font=f;
   	if (p->font && gc) XSetFont(display(),gc,p->font->fid);
}
void pgadget::checkup(void)
{
//	if (!o->font) o->font=dfont();
	warn_printf("checkup-call no longer required!\n",o);
}
XFontStruct *pgadget::dfont(void)
{
	XFontStruct *f;
	if (f=FindFont(DEFAULTFONT)) return f;
	if (f=XLoadQueryFont(o->display(),DEFAULTFONT))
	{
		fontAdd(DEFAULTFONT,f);
		return f;
	}
	eprintf("No default X font found, terminating!");
	exit(10);
}
void gadget::CopyFont(gadget *g)
{
	if (g->p->fontnr) p->fontnr=g->p->fontnr; else p->font=g->p->font;
}

void gadget::SetEvents(unsigned int em)
{
	p->eventmask=em;
	Mode(p->mode);
}

void gadget::Resize(int newwidth,int newheight)
{
    width=newwidth; height=newheight;
    XResizeWindow(display(),win,width,height);
}
void gadget::Move(int newx,int newy)
{
    x=newx; y=newy;
    XMoveWindow(display(),win,x,y);
}
void gadget::Lock(void)
{
	if ((active & GA_BUTTONPRESS)|(!(p->mode & GM_LOCKATONCE)&&(active & GA_ABOVE)))
	{
		p->locked_bak=1;
	}
	else if (active & GA_ABOVE)
	{
		active&=~GA_ABOVE;
	    if (win)
	    {
	    	class gadget *gsendto;

			gsendto=p->FindReceiver();
	    	p->GBRelease(NULL,gsendto,GB_ABOVE | GB_KEY | GB_BUTTON);
	    }
		p->locked_bak=1;	    		
		p->Glock();
	}
	else
	{
		p->locked_bak=1;	
		p->Glock();
	}
}
void gadget::Unlock(void)
{
	p->Gunlock();
}
void gadget::Block(int b)
{
	if ((active & GA_BUTTONPRESS)|(!(p->mode & GM_LOCKATONCE)&&(active & GA_ABOVE)))
	{
		p->blocked_bak=1;
	}
	else if (active & GA_ABOVE)
	{
		active&=~GA_ABOVE;
	    if (win)
	    {
	    	class gadget *gsendto;

			gsendto=p->FindReceiver();
	    	p->GBRelease(NULL,gsendto,GB_ABOVE | GB_KEY | GB_BUTTON);
	    }
		p->blocked_bak=1;	    		
		p->Gblock();
	}
	else
	{
		p->blocked_bak=1;	
		p->Gblock();
	}
}
void gadget::Unblock(int b)
{
	p->Gunblock();
}
void gadget::ActivateKey(void)
{
	if (p->blocked) return;
	if (locked) return;
	if (keyactive!=this)
	{
	    if (p->mode&GM_KEY)
    	{
			if (!onlywindow || IsParentWindow(display(),win,onlywindow))
			{
				class gadget *gsendto;

				if (keyactive)
				{
					Xwindows *xw=(Xwindows*)keyactive->ParentClassType("Xwindows");
					if (xw)
					{
						xw->Keyactive(keyactive);
	 				}
		    	    keyactive->DeactivateKey();
				}

				gsendto=p->FindReceiver();
    		    keyactive=this;
        		active|=GA_KEY;
		   	    p->GBPress(NULL,gsendto,GB_KEY);
				if (p->ic) XSetICFocus(p->ic);
			}
		}
    }
}
void gadget::DeactivateKey(void)
{
    if (keyactive==this)
    {
		class gadget *gsendto;

       	active&=~GA_KEY;
        if (keyactive->win)
        {
			gsendto=p->FindReceiver();
        	p->GBRelease(NULL,gsendto,GB_KEY);
        }
        keyactive=NULL;
    }
}

// create function, should be overloaded by child classes
// and then finally called by this new function!
int gadget::Create(void)
{ 
	XSetWindowAttributes xwa;
	char *w;
	int val;

	if (p->gpopup)
	{
		if (!p->gpopup->parent) p->gpopup->parent=parent;
		p->gpopup->Create();
	}

	if (w=GetResource("winbackground"))
	{
		Background(AllocColor(w));
	}
	if (w=GetResource("weight"))
	{
		weight=atoi(w);
	}
	if (w=GetResource("locked"))
	{
		if (!strcasecmp(w,"True"))
		{
			Lock();
		}
	}

	white=WhitePixel(display(),screen());
	black=BlackPixel(display(),screen());
	if (p->background==(unsigned long)-1) p->background=white;
	if (p->foreground==(unsigned long)-1) p->foreground=black;

	if (width==0) warn_printf("This gadget object will get a width of 0 - this will fail!",this);
	if (height==0) warn_printf("This gadget object will get a height of 0 - this will fail!",this);

    if (win=XCreateSimpleWindow(display(),parent,x,y,width,height,0,p->foreground,p->background))
    {
    	XClassHint classhint;
		classhint.res_name =Name();
		classhint.res_class=FullName(); // "Xclasses_gadget";
		XSetClassHint(display(),win,&classhint);

        if (gc=XCreateGC(display(),win,0,0))
        {
#ifdef GG_BACKING_STORE
			if (w=GetResource("backingstore"))
			{
				if (!strcasecmp(w,"True"))
				{
		        	xwa.backing_store=GG_BACKING_STORE;
    		       	XChangeWindowAttributes(display(),win,CWBackingStore,&xwa);
				}
			}
			else
			{
	        	xwa.backing_store=GG_BACKING_STORE;
    	       	XChangeWindowAttributes(display(),win,CWBackingStore,&xwa);
			}
#endif
			if (xim && icWorking)
			{
				p->ic=XCreateIC(xim,
							XNInputStyle, best_style,
							XNClientWindow, win,
							NULL);
				if (!p->ic) icWorking=0;
			}
            Mode(p->mode);
            if (p->cursor)
            {
                XDefineCursor(display(),win,p->cursor);
            }
           	if (gg_font()) XSetFont(display(),gc,gg_font()->fid);
            XMapRaised(display(),win);
            p->AddGG();

			for(int i=0;i<p->key_count;i++)
	           	ApplyKey(p->key_key[i],p->key_qualifier[i]);
			p->key_count=0;
			for(int i=0;i<p->xkey_count;i++)
	           	ApplyXKey(p->xkey_key[i],p->xkey_qualifier[i]);
			p->xkey_count=0;

			for(int i=1;i<=5;i++)
			{
				char buf[10];
				sprintf(buf,"button%d",i);
				w=GetResource(buf);
				if (w)
				{
					val=atoi(w);
					if (val>=1 && val<=5)
						p->buttonmap[i-1]=val;
				}
			}
            return 1;
        }
        else
        {
            XDestroyWindow(display(),win);
        }
    }
    return 0;
}

// functions for child classes (mostly called by their CreateGadget() )
void gadget::MCursor(int c) 
{
	if (!p->blocked)
	{
	    if (p->cursor)
    	{
        	if (win) XUndefineCursor(display(),win);
	        XFreeCursor(display(),p->cursor);
    	    p->cursor=0;
	    }
	    if (c>=0 && (p->cursor=XCreateFontCursor(display(),c)))
    	{
        	if (win)
	        {
    	        XDefineCursor(display(),win,p->cursor);
        	}
	    }
	}
    p->oldcursor=c;
}
void gadget::Mode(int m)
{
	Mode((unsigned int)m);
}
void gadget::Mode(unsigned int m)
{
	if (p->ic)
	{
		XGetICValues(p->ic,XNFilterEvents,&p->events,NULL);
	}
	else
		p->events=0;
    p->events|=p->eventmask|ButtonPressMask|ButtonReleaseMask|EnterWindowMask|LeaveWindowMask|ExposureMask|StructureNotifyMask;

	if (m & GM_OWNERGRAB)
	{
		p->events|=OwnerGrabButtonMask;
	}
    if (m & GM_MOUSEMOVE)
    {
        p->events|=ButtonMotionMask;
    }
//	if (m & GM_KEY)
    {
        p->events|=KeyPressMask;
    }

    if (win)
    {
        XSelectInput(display(),win,p->events);
    }
	if (m & GM_CUTPASTE && !p->cutprop && win)
	{
		p->cutprop=XInternAtom(display(),"Xclasses_clipboard_property_(Xclasses by J.Schmitz)",False);
	}

    p->mode=m;
}

KeySym gadget::LookupKeysym(XEvent *event)
{
	return XLookupKeysym((XKeyEvent*)event,0);
}
int gadget::LookupString(XEvent *event,char *buffer_return,int buffer_size,KeySym &keysym,int &status)
{
	ggs *g=FindGG(event->xkey.window,GT_GADGET);

	int r=0;
	if (g && g->ggclasse->p->ic)
	{
		keysym=0;
		r=XmbLookupString(g->ggclasse->p->ic,(XKeyEvent*)event,buffer_return,buffer_size,&keysym,&status);
		if (status==XBufferOverflow)
		{
			XmbResetIC(g->ggclasse->p->ic);
			warn_printf("gadget::LookupString buffer overflow",this);
		} //printf("IC: %d, %x, %s\n",r,keysym,buffer_return);
	}
	else
	{
	    XComposeStatus compose_status = {NULL, 0};
		status=XLookupBoth;
		r=XLookupString((XKeyEvent*)event,buffer_return,buffer_size,&keysym,&compose_status);
// printf("none IC: %x, %s\n",keysym,buffer_return);
 	}
	return r;
}
int gadget::DefaultKeyHandler(XEvent *event)
{
	int r=DKH_NOKEYEVENT;
	if (!event) return r;
	if (event->type==KeyPress)
	{
//		int c;
		char taste[5];
		KeySym key;

		r=DKH_NOTHING;
		key=XLookupKeysym((XKeyEvent*)event,0);

		switch (key)
		{
#ifdef XK_ISO_Left_Tab
			case XK_ISO_Left_Tab:
#endif
			case XK_Tab: // next gg
				// nächste Key-GG suchen, -> ActivateKey();
				{
					Xwindows *xw1=NULL,*xw2=NULL;
					struct ggs *g;
					int mark=0;
					if (keyactive && this!=&dummy)
					{
						gadget *gg=this;
						while (gg->p->sendto)
						{
							gg=gg->p->sendto;
						}
						g=gg->p->myg;
						xw1=(Xwindows*)g->classe->ParentClassType("Xwindows");
					}
					else
					{
						g=NULL;
						if (focuswindow)
							xw1=(Xwindows*)GetXwindowsOf(display(),focuswindow);
					}

					XWindowAttributes xwa;
					int ok;
					if (event->xkey.state & ShiftMask)
					{
						do
						{
							if (!(g=nohash_PrevGG(g)))
							{
								g=nohash_PrevGG(g);
								mark++;
							}
							ok=0;
							if (g->classe->p->mode & GM_KEY && !g->classe->locked && !g->classe->p->blocked && !g->classe->p->sendto)
							{
								xw2=(Xwindows*)g->classe->ParentClassType("Xwindows");
								
								if (xw1==xw2)
								{
									if (!onlywindow || IsParentWindow(display(),g->classe->win,onlywindow))
									{
										XGetWindowAttributes(display(),g->classe->win,&xwa);
										if (xwa.map_state==IsViewable) ok=1;
									}
								}
							}
						}
						while (!ok && mark<2);
					}
					else
					{
						do
						{
							if (!(g=nohash_NextGG(g)))
							{
								g=nohash_NextGG(g);
								mark++;
							}
							ok=0;
							if (g->classe->p->mode & GM_KEY && !g->classe->locked && !g->classe->p->blocked && !g->classe->p->sendto)
							{
								xw2=(Xwindows*)g->classe->ParentClassType("Xwindows");
								
								if (xw1==xw2)
								{
									if (!onlywindow || IsParentWindow(display(),g->classe->win,onlywindow))
									{
										XGetWindowAttributes(display(),g->classe->win,&xwa);
										if (xwa.map_state==IsViewable) ok=1;
									}
								}
							}
						}
						while (!ok && mark<2);
					}
					if (mark<2) g->classe->ActivateKey();
				}
				r=DKH_DEACTIVATED;
				break;
			case XK_Return:
			case XK_KP_Enter: // select
				if ((r=KeyHandler(this,event,key,taste))==DKH_NOTHING)
				{
					r=DKH_SELECT;
				}
				break;
			default:
				if (this==&dummy && focuswindow)
					r=KeyHandler(GetXwindowsOf(display(),focuswindow),event,key,taste);
				else
					r=KeyHandler(this,event,key,taste);
				break;
		}
	}
	if (r==DKH_DEACTIVATED)
	{
		if (keyactive==this) r=DKH_INTERNAL;
	}
	return r;
}
void gadget::SendEventTo(class gadget *to)
{
	p->sendto=to;
}
void gadget::Background(unsigned long gbackcolor)
{
    p->background=gbackcolor;
    if (win)
    {
		XSetWindowBackground(display(),win,gbackcolor);
		XClearWindow(display(),win);
    }
}
unsigned long gadget::Background(void)
{
	return p->background;
}
void gadget::SetKeyHandler(int h)
{
	if (!win) p->keyhandlermode=h;
}
int gadget::GetKeyHandler(void)
{
	return p->keyhandlermode;
}
#define MAXGRAB	10
struct gggrab
{
	gadget			*gg;
	Bool			owner_events;
	unsigned int	event_mask;
	int				pointer_mode;
	int				keyboard_mode;
	Window			confine_to;
	Cursor			cursor;
};
static struct gggrab grabedgg[MAXGRAB];
static int grabedcc=0;
int gadget::GrabPointer(Bool owner_events,unsigned int event_mask,int pointer_mode,int keyboard_mode,Window confine_to,Cursor cursor)
{
	int g=0;
#ifndef DEBUG
	g=XGrabPointer(display(),
					win,
					owner_events,
					event_mask,
					pointer_mode,
					keyboard_mode,
					confine_to,
					cursor, //XC_arrow,
					CurrentTime);
	if (g==GrabSuccess) p->grabed=1; else p->grabed=0;
	grabedgg[grabedcc].gg=this;
	grabedgg[grabedcc].owner_events=owner_events;
	grabedgg[grabedcc].event_mask=event_mask;
	grabedgg[grabedcc].pointer_mode=pointer_mode;
	grabedgg[grabedcc].keyboard_mode=keyboard_mode;
	grabedgg[grabedcc].confine_to=confine_to;
	grabedgg[grabedcc].cursor=cursor;
	grabedcc++;
#else
	p->grabed=0;
#endif
	IgnoreExcept(0);
	return g;
}
void gadget::UngrabPointer(void)
{
	IgnoreNothing();
	if (p->grabed)
	{
		XUngrabPointer(display(),CurrentTime);
		grabedcc--;
	}
	if (grabedcc>0)
	{
		grabedcc--;
		grabedgg[grabedcc].gg->GrabPointer(
			grabedgg[grabedcc].owner_events,
			grabedgg[grabedcc].event_mask,
			grabedgg[grabedcc].pointer_mode,
			grabedgg[grabedcc].keyboard_mode,
			grabedgg[grabedcc].confine_to,
			grabedgg[grabedcc].cursor);
	}
}
void gadget::HelpText(char *t)
{
	p->helptext=t;
}
void gadget::ContextPopup(class popup *po)
{
	p->gpopup=po;
	po->ParentClass(this);
}
void gadget::ApplyKey(char key,int qualifier)
{
	if (!win)
	{
		p->key_key[p->key_count]=key;
		p->key_qualifier[p->key_count]=qualifier;
		p->key_count++;
		if (p->key_count>=MAXKEY) p->key_count=MAXKEY-1;
	}
	else
	{
		Xwindows *xw=(Xwindows*)ParentClassType("Xwindows");
		if (xw && p->keyhandlermode && key)
		{
			char keya[2];
			keya[0]=key;
			keya[1]=0;
			switch (p->keyhandlermode)
			{
				case KHM_ACTIVATE:
					xw->Keyclass()->SendActivateTo(keya,qualifier,this);
					break;
				case KHM_SELECT:
					xw->Keyclass()->SendSelectTo(keya,qualifier,this);
            		break;
            	case KHM_SENDTO:
            		xw->Keyclass()->SendKeyTo(keya,qualifier,this);
            		break;
            	case KHM_HANDLER:
            		xw->Keyclass()->SendHookTo(keya,qualifier,p,(SENDHOOK)&pgadget::GKHandler,0);
            		break;
            	case KHM_HANDLERSELECT:
            		xw->Keyclass()->SendHookSelectTo(keya,qualifier,this,p,(SENDHOOK)&pgadget::GKHandler,0);
            		break;
			}
		}
	}
}
void gadget::ApplyXKey(int xkey,int qualifier)
{
	if (!win)
	{
		p->xkey_key[p->xkey_count]=xkey;
		p->xkey_qualifier[p->xkey_count]=qualifier;
		p->xkey_count++;
		if (p->xkey_count>=MAXKEY) p->xkey_count=MAXKEY-1;
	}
	else
	{
		Xwindows *xw=(Xwindows*)ParentClassType("Xwindows");
		if (xw && p->keyhandlermode && xkey)
		{
			switch (p->keyhandlermode)
			{
				case KHM_ACTIVATE:
					xw->Keyclass()->SendActivateTo(xkey,qualifier,this);
					break;
				case KHM_SELECT:
					xw->Keyclass()->SendSelectTo(xkey,qualifier,this);
           			break;
	           	case KHM_SENDTO:
    	       		xw->Keyclass()->SendKeyTo(xkey,qualifier,this);
        	   		break;
           		case KHM_HANDLER:
	           		xw->Keyclass()->SendHookTo(xkey,qualifier,p,(SENDHOOK)&pgadget::GKHandler,0);
    	       		break;
        	   	case KHM_HANDLERSELECT:
           			xw->Keyclass()->SendHookSelectTo(xkey,qualifier,this,p,(SENDHOOK)&pgadget::GKHandler,0);
           			break;
			}
		}
	}
}
static char	*cutdata=NULL;
static int	cutsize=0;

void gadget::ClipTo(char *data,int size)
{
// set new, deselect old
	if (p->mode & GM_CUTPASTE)
	{
		Window w;
		w=XGetSelectionOwner(display(),XA_PRIMARY);
		if (w!=None && w!=win)
		{
			if (FindGG(w,GT_ANY))
			{
				XEvent ev;
				ev.type						= SelectionClear;
				ev.xselectionclear.window	= w;
				ev.xselectionclear.selection= XA_PRIMARY;
				ev.xselection.time			= time(NULL);
				XSendEvent(display(),w,False,0,&ev);
			}
		}
		if (data)
		{
			w=win;
			if (cutdata) delete cutdata;
			cutsize=size;
			cutdata=new char[size];
			memcpy(cutdata,data,size);
		}
		else
		{
			w=None;
			cutsize=0;
		}
		XSetSelectionOwner(display(),XA_PRIMARY,w,CurrentTime);
	}
}
void gadget::ClipRequest(void)
{
//	request
	if (p->mode & GM_CUTPASTE)
	{
		if (XGetSelectionOwner(display(),XA_PRIMARY))
		{
			XConvertSelection(display(),XA_PRIMARY,XA_STRING,p->cutprop,win,CurrentTime);
		}
	}
}

static Window ignore_arr[40]={0,};
static int ignore_top=0;
void gadget::IgnoreExcept(Window w)
{
	ignore_arr[ignore_top++]=onlywindow;
	onlywindow=w;
	if (keyactive && onlywindow)
	{
		if (!IsParentWindow(display(),keyactive->win,onlywindow))
		{
			keyactive->DeactivateKey();
		}
	}
}
void gadget::IgnoreNothing(void)
{
	if (ignore_top>0)
		onlywindow=ignore_arr[--ignore_top];
	else
		onlywindow=0;
}

// default functions, should be rediffined (overloaded) by child classes
void gadget::GExpose(XEvent *event)
{
	if (wasfor)
	{
		wasfor->GExpose(event);
	}
	else
	{
//	    XSetForeground(display(),gc,black);
//    	XDrawRectangle(display(),win,gc,0,0,width-1,height-1);
    }
}
void gadget::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor)
	{
		wasfor->GActionPress(mx,my,rx,ry,state,button,reason);
	}
	else
	{
//	    XSetForeground(display(),gc,black);
//    	XFillRectangle(display(),win,gc,2,2,width-4,height-4);
	}
}
void gadget::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor)
	{
		wasfor->GActionRelease(mx,my,rx,ry,state,button,reason);
	}
	else
	{
//	    XSetForeground(display(),gc,white);
//    	XFillRectangle(display(),win,gc,2,2,width-4,height-4);
    }
}
int gadget::GMouseMove(int mx,int my,int rx,int ry,unsigned int state,int button)
{
	if (wasfor)
	{
		return wasfor->GMouseMove(mx,my,rx,ry,state,button);
	}
	else
	{
		return 0;
	}
}
int gadget::GActivate(XEvent *event,int why)
{
	if (wasfor)
	{
		return wasfor->GActivate(event,why);
	}
	else
	{
		int r;
		r=DefaultKeyHandler(event);
		if (r==DKH_NOKEYEVENT)
		{
			if (p->mode&GM_KEY && why==GA_BUTTONPRESS) ActivateKey();
		    return GAR_ACTIVE;   // always active
		}
	    if (r==DKH_SELECT)
    	{
	    	return GAR_ACTIVE;
    	}
	    return 0;
	}
}
int gadget::GSelected(XEvent *event,int why,int wasabove)
{
	if (wasfor)
	{
		return wasfor->GSelected(event,why,wasabove);
	}
	else
	{
	    return (wasabove|(why==GA_KEY))?GSR_SELECTION|GSR_MESSAGE:0;   // selected if above
	}
}
void gadget::GSetLimits(void)
{
	setlimit=1;
	// nolimits
}
int gadget::GClipHandler(int why,char *data,int size)
{
	if (wasfor)
	{
		return wasfor->GClipHandler(why,data,size);
	}
	else
	{
	    return why==CH_REQUEST?GCH_MESSAGE:0;
	}
}
int gadget::GKeyHandler(XEvent *event,KeySym k,char *c)
{
	if (wasfor)
	{
		return wasfor->GKeyHandler(event,k,c);
	}
	else
	{
	    return 0;
	}
}
int pgadget::allevents(XEvent *myevent)
{
	int r=0;
	if (mode & GM_ALLEVENTS)
	{
	    class gadget *gsendto;
		gsendto=FindReceiver();
		if (GBActivate(myevent,GA_EVENT,gsendto))
		{
			r=-1;
			if (GBSelected(myevent,GA_EVENT,0,gsendto))
			{
				lastgg=o;
				r=gsendto->p->id;
				r=(gsendto->p->funccall()?r:-1);
			}
		}
	}
	return r;
}
// ------------------------------------------------------------------------

#define DCLICK	defaults.dclickdelay	// Millisekunden

static Time lastklick=0;
static int lastx,lasty=0;
static int dklick=0;
static int doppelklick(XEvent *ev)
{
	int w=0;
	if (ev->xbutton.time-lastklick<DCLICK && 
		ev->xbutton.x>=lastx-3 && ev->xbutton.x<=lastx+3 &&
		ev->xbutton.y>=lasty-3 && ev->xbutton.y<=lasty+3)
	{
		w=1;
	}
	lastklick=ev->xbutton.time;
	lastx=ev->xbutton.x;
	lasty=ev->xbutton.y;
	return w;
}
// WARNUNG: Funktionen sind unabhaengig von Klasse; THIS - NICHT BEACHTEN!!

int gadget::DoubleClick(void)
{
	return dklick;
}
gadget *gadget::LastSelected(void)
{
	return lastgg;
}
int gadget::HandleEvent(XEvent *myevent)
{	// 0 - unused, -1 - used, >0 - ID
	aw_unalarm();

    ggs *g;
    class gadget *gsendto;
    int r=0;

	dklick=0;

	lastgg=NULL;
	if (!myevent)
	{ // Tick
		if (currenthelp && !tickactive)
		{ // nur nach Anzahl Ticks öffnen
			currenthelp->p->OpenHelp();
		}

		ticklist.callback();

		g=NULL;
		while (g=NextGG(g,GT_GADGET))
		{
			if ((g->ggclasse->p->mode & GM_NORMALTICK)&&(g->ggclasse!=tickactive)&&(g->ggclasse!=keyactive))
			{
				gsendto=g->ggclasse->p->FindReceiver();
				g->ggclasse->active|=(g->ggclasse->p->GBActivate(NULL,GA_TICK,gsendto)?GA_TICK:0);
				if (g->ggclasse->active & GA_TICK)
				{
					g->ggclasse->active&=~GA_TICK;
                    g->ggclasse->p->GBSelected(myevent,GA_TICK,1,gsendto);
				}
			}
		}

		if (keyactive)
		{
// müßte unnötig geworden sein, durch andere Checks - erzeugt hohe Netzlast!
//			if (!onlywindow || IsParentWindow(g_display(),keyactive->win,onlywindow))
			{
				if (keyactive->p->mode & GM_KEYTICK)
				{
					gsendto=keyactive->p->FindReceiver();
					keyactive->active|=(keyactive->p->GBActivate(NULL,GA_TICK,gsendto)?GA_TICK:0);
					if (keyactive->active & GA_TICK)
					{
						keyactive->active&=~GA_TICK;
        	            keyactive->p->GBSelected(myevent,GA_TICK,1,gsendto);
					}
				}
			}
		}

		if (tickactive)
		{
			if (tickactive->p->mode & GM_ACTIVETICK)
			{
				gsendto=tickactive->p->FindReceiver();
				tickactive->active|=(tickactive->p->GBActivate(NULL,GA_TICK,gsendto)?GA_TICK:0);
				if (tickactive->active & GA_TICK)
				{
					tickactive->active&=~GA_TICK;
                    if (tickactive->p->GBSelected(myevent,GA_TICK,1,gsendto))
                    {
                        // Auswahl
            	        r=gsendto->p->id;
                       	r=(gsendto->p->funccall()?r:-1);
                        lastgg=gsendto;
                    }
				}
			}
		}
		return r;
	}
// 	if (grabedcc>0)
// 	{
// 		int dx,dy;
// 		Window dw,dwo;
// 		Window dw,dwo;
// 		dw=dwo=((XButtonEvent*)myevent)->window;
// 		do
// 		{
// 			dwo=dw;
// 			XTranslateCoordinates(((XButtonEvent*)myevent)->display,((XButtonEvent*)myevent)->window,dwo,((XButtonEvent*)myevent)->x,((XButtonEvent*)myevent)->y,&dx,&dy,&dw);
// 		}
// 		while (dw && dw!=((XButtonEvent*)myevent)->window && dw!=dwo);
// 		dw=dwo; 
// 		if (dw && dw!=((XButtonEvent*)myevent)->window)
// 		{
// 			switch (myevent->type)
// 			{
// 		        case MotionNotify:
// 		        case ButtonPress:
// 		        case ButtonRelease:
// 		        case EnterNotify:
// 				case LeaveNotify:
// 					XTranslateCoordinates(((XButtonEvent*)myevent)->display,((XButtonEvent*)myevent)->window,dw,((XButtonEvent*)myevent)->x,((XButtonEvent*)myevent)->y,&dx,&dy,&dwo);
// 					((XButtonEvent*)myevent)->x=dx;
// 					((XButtonEvent*)myevent)->y=dy;
// 					((XButtonEvent*)myevent)->window=dw;
// 					break;
// 			}
// 		}
// 	}

	if (XFilterEvent(myevent,None)) return 0;

	if (onlywindow 
		&& myevent->type!=Expose 
		&& myevent->type!=NoExpose 
		&& myevent->type!=GraphicsExpose
		&& myevent->type!=FocusIn 
		&& myevent->type!=FocusOut 
		&& myevent->type!=MappingNotify
		&& myevent->type!=KeyPress
		&& (!FindGG(myevent->xexpose.window,GT_ANY) || !IsParentWindow(g_display(),myevent->xexpose.window,onlywindow))) return 0;

    switch (myevent->type)
    {
        case Expose:
        case NoExpose:
        case GraphicsExpose:
            if (g=FindGG(myevent->xexpose.window,GT_ANY))
            {
            	int exc=0;
                r=-1;
            	// XCheckWindowEvent(g_display(),g->ggclasse->win,mask,myevent);
            	// XCheckTypedWindowEvent(g_display(),g->ggclasse->win,type,myevent);
            	if (g->ggclasse->p->mode & GM_ONEEXPOSE && myevent->type==Expose)
            	{ // Expose minimieren
	            	while (XCheckTypedWindowEvent(g_display(),g->ggclasse->win,Expose,myevent)) 
	            	{
	            		if (myevent->xexpose.count==0) exc++;
	            	}
				}
                if ((!(g->ggclasse->p->mode&GM_ONEEXPOSE))||(myevent->xexpose.count==0)||(exc>0)||(myevent->type!=Expose))
   	            {
//	   	            XWindowAttributes xwa;
//					XGetWindowAttributes(g_display(),g->ggclasse->win,&xwa);
//					if (xwa.map_state==IsViewable)
					{
						gsendto=g->ggclasse->p->FindReceiver();
						g->ggclasse->p->GBExpose(myevent,gsendto);
					}
//					else
//					{ // weitere Exposes dafuer?
//		            	while (XCheckTypedWindowEvent(g_display(),g->ggclasse->win,Expose,myevent))  ;
//					}
                }
            }
            break;
        case MappingNotify:
			if (((XMappingEvent*)myevent)->request==MappingKeyboard)
	            XRefreshKeyboardMapping((XMappingEvent*)myevent);
            r=-1;
            break;
        case ButtonPress:
//        	printf("BPress %x\n",myevent->xbutton.window);
           	if (currenthelp)
           	{ // Help zu
				currenthelp->p->CloseHelp();
           		currenthelp=NULL;
           	}
            if (g=FindGG(myevent->xbutton.window,GT_GADGET))
            {
#ifdef DEBUG
                printf("Resource: %s\n",g->ggclasse->FullName());
#endif

				Xwindows *pwin=(Xwindows*)g->ggclasse->ParentClassType("Xwindows");
				if (pwin)
				{
					char buf[20];
					char *w;
					sprintf(buf,"button%dredirect",myevent->xbutton.button);
					if (w=g->ggclasse->GetResource(buf))
					{
						gadget *gg;
						if (w[0]=='*')
						{
							Xclasses *g2=g->ggclasse;
							do
							{
								gg=(gadget*)FindObject(g2->FullName(),w+1);
								g2=g2->ParentClass();
							}
							while (g2 && !gg);
						}
						else
							gg=(gadget*)FindObject(w);
						if (gg && gg->win)
						{
							g=FindGG(gg->win,GT_GADGET);
						}
					}
					else if (w=pwin->GetResource(buf))
					{
						gadget *gg;
						if (w[0]=='*')
						{
							Xclasses *g2=g->ggclasse;
							do
							{
								gg=(gadget*)FindObject(g2->FullName(),w+1);
								g2=g2->ParentClass();
							}
							while (g2 && !gg);
						}
						else
							gg=(gadget*)FindObject(w);
						if (gg && gg->win)
						{
							g=FindGG(gg->win,GT_GADGET);
						}
					}
				}

            	if (helpmode)
            	{ // helpmode für später


            		helpmode=0;
            	}
            	else if (g->ggclasse->p->ButtonAllowed(myevent->xbutton.button))
                {
	            	if (!g->ggclasse->locked)
    	        	{
        	        	gsendto=g->ggclasse->p->FindReceiver();            	
    	                g->ggclasse->p->button	=myevent->xbutton.button;
        	            g->ggclasse->p->keystate=myevent->xbutton.state;
						if (doppelklick(myevent))
						{
	    	            	g->ggclasse->active|=GA_DCLICK;
						}
						int rr=(g->ggclasse->p->GBActivate(myevent,GA_BUTTONPRESS,gsendto)?GA_BUTTONPRESS:0);
					 	g->ggclasse->active|=rr;
                	    if (g->ggclasse->active&GA_BUTTONPRESS)
                    	{
                    		tickactive=g->ggclasse;
	    	            	g->ggclasse->state=1;
    	    	            g->ggclasse->p->GBPress(myevent,gsendto,GB_BUTTON);
	                    }
    	            }
	    	        else
    		        {
    		        	XBell(g_display(),-75);
    	    	    }
				}
				else if (myevent->xbutton.button==3)
				{
					ggs *g2=g;
					while (g2 && !(g2->ggclasse->p->gpopup && !g2->ggclasse->locked && !g2->ggclasse->p->blocked))
					{
						g2=FindGG(g2->ggclasse->parent,GT_GADGET);
					}
					if (g2)
					{
						int x,y;
						GetPositionOnRoot(g_display(),g->ggclasse->win,x,y);
						x+=myevent->xbutton.x;
						y+=myevent->xbutton.y;
						g2->ggclasse->p->gpopup->Open(x,y);
						g2->ggclasse->p->gpopup->active|=GA_BUTTONPRESS;
						contextactive=g2->ggclasse->p->gpopup;
					}
				}
                r=-1;
            }
            break;
        case ButtonRelease:
//        	printf("BRelease %x (%x)\n",myevent->xbutton.window,tickactive);
        	{
// #define WELCHES tickactive
        		gadget *WELCHES=NULL;
				if (contextactive)
				{
					WELCHES=contextactive;
					contextactive=NULL;
				}
				else if (tickactive)
				{
					WELCHES=tickactive;
					tickactive=NULL;
				}
				if (WELCHES)
        	    {
            	    r=-1;
                	if ((WELCHES->p->ButtonAllowed(myevent->xbutton.button)) && (WELCHES->active&GA_BUTTONPRESS))
	                {
    	                int wasabove=WELCHES->state;
        	        	gsendto=WELCHES->p->FindReceiver();

            	        WELCHES->active		&=~GA_BUTTONPRESS;
    	        	    WELCHES->p->button  =myevent->xbutton.button;
        	        	WELCHES->p->keystate=myevent->xbutton.state;

	                    WELCHES->p->GBRelease(myevent,gsendto,GB_BUTTON);
    		            WELCHES->state=0;
        	            if (WELCHES->p->GBSelected(myevent,GA_BUTTONPRESS,wasabove,gsendto))
            	        {
                	        // Auswahl
	        	            if (WELCHES->p->CheckVars()==GA_DCLICK)
    	        	        {
        	        	    	dklick=1;
            	        	}
	            	        WELCHES->active&=~GA_DCLICK;
	            	        r=gsendto->p->id;
                        	r=(gsendto->p->funccall()?r:-1);
	                        lastgg=gsendto;
	                        if (!objectExists(WELCHES)) WELCHES=NULL;
    	                }
    	                else
    	                {
							WELCHES->p->CheckVars();
	            	        WELCHES->active&=~GA_DCLICK;
    	                }
    	                if (WELCHES) WELCHES->p->Glock();
        	        }
				}
            }
            break;
        case EnterNotify:
//         	if (myevent->xcrossing.focus) keyboard_active=1; else keyboard_active=0;

//        	printf("Enter %x/%d\n",myevent->xcrossing.window,myevent->xcrossing.focus);
            if (g=FindGG(myevent->xcrossing.window,GT_GADGET))
            {
           		if (currenthelp )// && currenthelp!=g->ggclasse)
           		{ // altes Help zu
					currenthelp->p->CloseHelp(1);
					currenthelp=NULL;
           		}
				// Enter-Leave Paar minimieren
				int good_event=1;
            	if (XEventsQueued(g_display(),QueuedAfterReading)>0)
            	{
            		XEvent ahead;
            		XPeekEvent(g_display(),&ahead);
            		if (ahead.type==LeaveNotify &&
            			ahead.xcrossing.window==myevent->xcrossing.window &&
            			ahead.xcrossing.mode==myevent->xcrossing.mode &&
            			ahead.xcrossing.detail==myevent->xcrossing.detail)
            		{
            			XNextEvent(g_display(),myevent);
            			good_event=0;
            		}
            	}
            	if (good_event)
            	{
	                gsendto=g->ggclasse->p->FindReceiver();
    	            if (!(g->ggclasse->locked))
        	        {
            	    	if (g->ggclasse->p->mode & GM_ABOVE)
                		{
                			if (!(g->ggclasse->active & GA_ABOVE))
                			{
		                    	g->ggclasse->active|=(g->ggclasse->p->GBActivate(myevent,GA_ABOVE,gsendto)?GA_ABOVE:0);
	                		}
    	            	}
//						if (!g->ggclasse->active)
            	   		{
							currenthelp=g->ggclasse;
							// neues Help auf
							currenthelp->p->OpenHelp(1);
						}
    	            }
        	        if (g->ggclasse->active & (GA_ABOVE|GA_BUTTONPRESS))
            	    {
    	        	    g->ggclasse->state=1;
                    	g->ggclasse->p->GBPress(myevent,gsendto,GB_ABOVE);
	                }
				}
				r=-1;
            }
            break;
        case LeaveNotify:
//         	if (focus) keyboard_active=1; else keyboard_active=0;

//        	printf("Leave %x\n",myevent->xbutton.window);
            if (g=FindGG(myevent->xcrossing.window,GT_GADGET))
            {
            	if (currenthelp==g->ggclasse)
            	{ // Help zu
					currenthelp->p->CloseHelp(1);
            		currenthelp=NULL;
            	}
                if (g->ggclasse->active & (GA_ABOVE|GA_BUTTONPRESS))
                {
                	gsendto=g->ggclasse->p->FindReceiver();

                    if (g->ggclasse->active&GA_ABOVE)
                    {
	                    int wasabove=g->ggclasse->state;
	                    int allOk=1;
	                    
                        g->ggclasse->active&=~GA_ABOVE;
	                    g->ggclasse->p->GBRelease(myevent,gsendto,GB_ABOVE);
    	            	g->ggclasse->state=0;

                        if (g->ggclasse->p->GBSelected(myevent,GA_ABOVE,wasabove,gsendto))
                        {
                        	class gadget *bak=g->ggclasse;
                            // Auswahl
	            	        r=gsendto->p->id;
                        	r=(gsendto->p->funccall()?r:-1);
	                        if (!objectExists(bak)) allOk=0;
                        }
						if (allOk) g->ggclasse->p->Glock();
                    }
                    else
                    {
    	            	g->ggclasse->state=0;                    
	                    g->ggclasse->p->GBRelease(myevent,gsendto,GB_ABOVE);
                    }
                }
                r=-1;
            }
            break;
        case MotionNotify:
        	{
	            if (g=FindGG(myevent->xmotion.window,GT_GADGET))
    	        {
        	    	if (currenthelp==g->ggclasse)
            		{ // Help zu
						currenthelp->p->CloseHelp();
            			currenthelp=NULL;
	            	}
    	        	// Moves optimieren
        	    	// XCheckWindowEvent(g_display(),g->ggclasse->win,mask,myevent);
            		// XCheckTypedWindowEvent(g_display(),g->ggclasse->win,type,myevent);
	            	if (g->ggclasse->p->mode & GM_MINIMALMOVE)
		            	while (XCheckTypedWindowEvent(g_display(),g->ggclasse->win,MotionNotify,myevent)) ;
        	        gsendto=g->ggclasse->p->FindReceiver();
            	    if (!(g->ggclasse->locked) && (g->ggclasse->p->mode & GM_MOUSEMOVE) && (g->ggclasse->active & GA_BUTTONPRESS || g->ggclasse->p->mode & GM_ALLMOVES))
	                {                                
    	                if (g->ggclasse->p->GBMove(myevent,gsendto)) // && gsendto->p->func)
        	            {
            		        r=gsendto->p->id;
                	       	r=(gsendto->p->funccall()?r:-1);
	                    }
    	                lastgg=gsendto;
        	        }
	            }
			}
            break;
        case KeyPress:
           	if (currenthelp)
           	{ // Help zu
				currenthelp->p->CloseHelp();
           		currenthelp=NULL;
           	}
			if (keyactive)
			{
//				if (!onlywindow || IsParentWindow(g_display(),keyactive->win,onlywindow))
				{
					gsendto=keyactive->p->FindReceiver();
					r=keyactive->p->SendKey(myevent);
					if (r>0)
					{
						if (keyactive)
						{
	    	    	        if (keyactive->p->CheckVars()==GA_DCLICK)
    	    	    	    {
        	    	    	   	dklick=1;
	            		    }
	    	            	keyactive->active&=~GA_DCLICK;
    	    	        }
						r=gsendto->p->id;
						r=(gsendto->p->funccall()?r:-1);
			            lastgg=gsendto;
					}
				}
			}
			else
			{
				dummy.DefaultKeyHandler(myevent);
            }
            break;
		case FocusOut:
			focus=0;
			keyboard_active=0;
 			if (keyactive)
 			{
				Xwindows *xw=(Xwindows*)keyactive->ParentClassType("Xwindows");
				if (xw)
				{
					xw->Keyactive(keyactive);
 					if (xw->window()==myevent->xfocus.window) keyactive->DeactivateKey();
 				}
			}
			break;
		case FocusIn:
 			if (keyactive)
 			{
				Xwindows *xw=(Xwindows*)keyactive->ParentClassType("Xwindows");
				if (xw)
				{
					xw->Keyactive(keyactive);
 					keyactive->DeactivateKey();
 				}
			}
			{
				focus=1;
				keyboard_active=1;
				focuswindow=myevent->xfocus.window;

				Xwindows *aw=GetXwindowsOf(g_display(),focuswindow);
				if (aw)
				{
					gadget *k=aw->Keyactive(NULL);
					if (k)
						k->ActivateKey();
				}
				else
				{
				}
			}
			break;
		case OwnJSEvent:
			if (!((gadget*)myevent->xkey.x)->p->blocked)
			{
				gsendto=((gadget*)myevent->xkey.x)->p->FindReceiver();

				// Auswahl
				r=gsendto->p->id;
				r=(gsendto->p->funccall()?r:-1);
				lastgg=gsendto;
			}
			break;
		case SelectionClear:
            if (g=FindGG(myevent->xselectionclear.window,GT_GADGET))
            {
				if ((g->ggclasse->p->mode & GM_CUTPASTE) && (myevent->xselectionclear.selection==XA_PRIMARY))
				{
	                gsendto=g->ggclasse->p->FindReceiver();
   	                g->ggclasse->p->GClipH(CH_DESELECT,NULL,0,gsendto);
				}
				else
	            	r=g->ggclasse->p->allevents(myevent);
			}
			break;
		case SelectionRequest:
            if (g=FindGG(myevent->xselectionrequest.owner,GT_GADGET))
            {
            	if (g->ggclasse->p->mode & GM_CUTPASTE)
            	{
					if (cutdata)
					{
						XEvent ev;

						ev.type					= SelectionNotify;
						ev.xselection.requestor	= myevent->xselectionrequest.requestor;
						ev.xselection.selection	= myevent->xselectionrequest.selection;
						ev.xselection.target	= myevent->xselectionrequest.target;
						ev.xselection.time		= myevent->xselectionrequest.time;
						ev.xselection.property	= myevent->xselectionrequest.property;
						if (myevent->xselectionrequest.selection==XA_PRIMARY &&
						    myevent->xselectionrequest.target==XA_STRING)
						{
							XChangeProperty(g_display(),
					    		            ev.xselection.requestor,
							                ev.xselection.property,
							                ev.xselection.target,
							                8,
					    		            PropModeReplace,
					            		    (unsigned char*)cutdata,
							                cutsize);
						}
						else
						{
							ev.xselection.property=None;
							fprintf(stderr,"A program requests for selected %s\n",XGetAtomName(g_display(),myevent->xselectionrequest.selection));
							fprintf(stderr,"as type %s! Maybe later Xclasses will handle this....\n",XGetAtomName(g_display(),myevent->xselectionrequest.target));
						}
						XSendEvent(g_display(),ev.xselection.requestor,False,0,&ev);
					}
				}
				else
	            	r=g->ggclasse->p->allevents(myevent);
			}
			break;
		case SelectionNotify:
            if (g=FindGG(myevent->xselection.requestor,GT_GADGET))
            {
            	if ((g->ggclasse->p->mode & GM_CUTPASTE) && (myevent->xselection.selection==XA_PRIMARY) && (myevent->xselection.property!=None))
				{
					Atom ret_type;
					int ret_format;
					unsigned long ret_len,ret_after=0;
					unsigned char *t;

					if (myevent->xselection.target!=XA_STRING)
					{
						fprintf(stderr,"Do you know why another program sends %s\n",XGetAtomName(g_display(),myevent->xselection.target));
						fprintf(stderr,"while I requested %s?\n",XGetAtomName(g_display(),XA_STRING));
					}

					do
					{
						XGetWindowProperty(g_display(),myevent->xselection.requestor,
										   myevent->xselection.property,ret_after,8192,False,
										   myevent->xselection.target,&ret_type,&ret_format,&ret_len,
										   &ret_after,&t);
						if (ret_len>0)
						{
		                	gsendto=g->ggclasse->p->FindReceiver();
		    	            if (!(g->ggclasse->locked))
			                {                                
	    		                g->ggclasse->p->GClipH(CH_REQUEST,(char*)t,ret_len,gsendto);
							}
							XFree(t);
						}
					}
					while (ret_after>0);
					XDeleteProperty(g_display(),myevent->xselection.requestor,myevent->xselection.property);
				}
				else
	            	r=g->ggclasse->p->allevents(myevent);
			}
			break;
		case ConfigureNotify:
            if (g=FindGG(myevent->xcrossing.window,GT_GADGET))
            {
				if (g->ggclasse->p->ic)
				{
	            	if (best_style & XIMPreeditArea)
					{
						XRectangle preedit_area;
						preedit_area.width=myevent->xconfigure.width*4/5;
						preedit_area.height=0;
						g->ggclasse->p->GetPreferredGeometry(XNPreeditAttributes,&preedit_area);
						preedit_area.x=myevent->xconfigure.width-preedit_area.width;
						preedit_area.y=myevent->xconfigure.height-preedit_area.height;
						g->ggclasse->p->SetGeometry(XNPreeditAttributes,&preedit_area);
					}
					if (best_style & XIMStatusArea)
					{
						XRectangle status_area;
						status_area.width=myevent->xconfigure.width/5;
						status_area.height=0;
						g->ggclasse->p->GetPreferredGeometry(XNStatusAttributes,&status_area);
						status_area.x=0;
						status_area.y=myevent->xconfigure.height-status_area.height;
						g->ggclasse->p->SetGeometry(XNStatusAttributes,&status_area);
					}
				}
			}
			break;
		default:
            if (g=FindGG(myevent->xcrossing.window,GT_GADGET))
            {
            	r=g->ggclasse->p->allevents(myevent);
			}
			break;
    }
    return r;
}
//#define TICKDELAY	100000U
#define TICKDELAY	90000U
static struct timeval lasttick={0,0};
static unsigned int nexttick(void)
{
	struct timeval t1;
	unsigned int t;

	gettimeofday(&t1,NULL);
	if ((t=(t1.tv_sec-lasttick.tv_sec)*1000000+(t1.tv_usec-lasttick.tv_usec))>=TICKDELAY)
	{
		lasttick=t1;
		return 0;
	}
	return TICKDELAY-t;
}

int pgadget::HEvent(XEvent *event,int m)
{
	XEvent ev,*pev=&ev;
	int r=0,fd;
	fd_set readfds,wrtfds,exfds;
	struct timeval to;

	if (event) pev=event;

	while (1)
	{
		if (!nexttick())
		{
			r=gadget::HandleEvent(NULL);
			if (r>0)
			{
				XFlush(g_display());
				return r;
			}
			if (m==3 && r==0) return -1;
		}
		while (XEventsQueued(g_display(),QueuedAfterReading)>0)
		{
			if (!XCheckTypedEvent(g_display(),EnterNotify,pev))
				if (!XCheckTypedEvent(g_display(),ButtonPress,pev))
					XNextEvent(g_display(),pev);
			r=gadget::HandleEvent(pev);
			if (r>0)
			{
				XFlush(g_display());
				return r;
			}
			if (r==0)
			{
				if (event)
				{
					XFlush(g_display());			
					return 0;
				}
			}
			if (!nexttick())
			{
				r=gadget::HandleEvent(NULL);
				if (r>0)
				{
					XFlush(g_display());
					return r;
				}
				if (m==3 && r==0) return -1;
			}
		}
		XFlush(g_display()); // ???

		{
			fd=ConnectionNumber(g_display());
			if (fdlist.maxfd<fd) fdlist.maxfd=fd;
//			FD_ZERO(&readfds); FD_ZERO(&wrtfds); FD_ZERO(&exfds);
			memcpy(&readfds,&fdlist.readfd,sizeof(fd_set));
			memcpy(&wrtfds,&fdlist.writefd,sizeof(fd_set));
			memcpy(&exfds,&fdlist.exceptfd,sizeof(fd_set));
			FD_SET(fd,&readfds);
			to.tv_sec=0;
//			to.tv_usec=TICKDELAY;
			to.tv_usec=nexttick()+1;
			switch (m)
			{
				case 1: // warten, Ticks intern
					if ((r=select(fdlist.maxfd+1,&readfds,&wrtfds,&exfds,&to))==0)
					{
						nexttick();
						r=gadget::HandleEvent(NULL);
						if (r>0)
						{
							XFlush(g_display());
							return r;
						}
					}
					break;
				case 2: // nicht warten
					to.tv_sec=0;
					to.tv_usec=0;
					if ((r=select(fdlist.maxfd+1,&readfds,&wrtfds,&exfds,&to))>0)
					{
						fdlist.callback(&readfds,&wrtfds,&exfds);
					}
					return -1;
					break;
				case 3: // warten, Ticks nach aussen (0)
					if ((r=select(fdlist.maxfd+1,&readfds,&wrtfds,&exfds,&to))==0)
					{
						nexttick();
						r=gadget::HandleEvent(NULL);
						if (r>0)
						{
							XFlush(g_display());
							return r;
						}
						if (r==0)
						{
							return -1;
						}
					}
					break;
			}
			if (r>0)
			{
				if (FD_ISSET(fd,&readfds))
				{ // für uns
				}
				fdlist.callback(&readfds,&wrtfds,&exfds);
			}
		}
	}
}
int gadget::NextEvent(XEvent *event) // warten, unbenutzte Events in event wenn !=NULL
{
	return pgadget::HEvent(event,1);
}; // 0 - Event, >0 - ID
int gadget::UpdateEvent(XEvent *event) // wenn Events bearbeiten, unbenutzte in event wenn !=NULL
{
	return pgadget::HEvent(event,2);
}; // 0 - Event, -1 - No Event, >0 - ID
int gadget::TickEvent(XEvent *event) // warten, unbenutze Events in event wenn !=NULL, Timeout nach 1/10 Sek
{
	return pgadget::HEvent(event,3);
}; // 0 - Event, -1 - Tick, >0 - ID

// ----------------------------------------------------------------
// ---- end of static functions -----------------------------------
// ----------------------------------------------------------------

int gadget::gg_minwidth(void)
{
	return minwidth;
}
int gadget::gg_maxwidth(void)
{
	return maxwidth;
}	
int gadget::gg_minheight(void)
{
	return minheight;
};
int gadget::gg_maxheight(void)
{
	return maxheight;
}
Window gadget::gg_win(void)
{
	return win;
}
GC gadget::gg_gc(void)
{
	return gc;
}
int gadget::gg_weight(void)
{
	return weight*10;
}
int gadget::gg_width(void)
{
	return width;
}
int	gadget::gg_height(void)
{
	return height;
}
int gadget::gg_before_x(void)
{
	return before_x;
}
int gadget::gg_minxaspect(void)
{
	return minxaspect;
}
int gadget::gg_minyaspect(void)
{
	return minyaspect;
}
int gadget::gg_maxxaspect(void)
{
	return maxxaspect;
}
int gadget::gg_maxyaspect(void)
{
	return maxyaspect;
}
int gadget::gg_active(void)
{
	return active;
}
int gadget::gg_locked(void)
{
	return locked;
}
unsigned int gadget::gg_mode(void)
{
	return p->mode;
}

// ----------------------------------------------------------------
// ----- gadget select callback -----------------------------------
// ----------------------------------------------------------------

void gadget::AddReadFD(int fd,void *c,...)
{
	va_list ap;
	va_start(ap, c);
	fdlist.addread(fd,c,(void* (*)(int))va_arg(ap,void *));
	va_end(ap);
}
void gadget::AddReadFD(int fd,callable *c,void (callable::*f)(int))
{
	fdlist.addread(fd,c,f);
}
void gadget::AddWriteFD(int fd,void *c,...)
{
	va_list ap;
	va_start(ap, c);
	fdlist.addwrite(fd,c,(void* (*)(int))va_arg(ap,void *));
	va_end(ap);
}
void gadget::AddWriteFD(int fd,callable *c,void (callable::*f)(int))
{
	fdlist.addwrite(fd,c,f);
}
void gadget::AddExceptFD(int fd,void *c,...)
{
	va_list ap;
	va_start(ap, c);
	fdlist.addexcept(fd,c,(void* (*)(int))va_arg(ap,void *));
	va_end(ap);
}
void gadget::AddExceptFD(int fd,callable *c,void (callable::*f)(int))
{
	fdlist.addexcept(fd,c,f);
}
void gadget::RemoveFD(int fd)
{
	fdlist.remfd(fd);
}

// ----------------------------------------------------------------
// ----- gadget tick callback -----------------------------------
// ----------------------------------------------------------------

void gadget::AddTicker(int mode,int delay,void (*f)(void))
{
	enum ticknode::ticktype m=ticknode::REGULARLY;
	if (mode==TM_ONCE)
		m=ticknode::ONCE;
	ticklist.addtick(f,delay,m);
}
void gadget::AddTicker(int mode,int delay,callable *c,void (callable::*f)(void))
{
	enum ticknode::ticktype m=ticknode::REGULARLY;
	if (mode==TM_ONCE)
		m=ticknode::ONCE;
	ticklist.addtick(c,f,delay,m);
}
void gadget::RemoveTicker(callable *c,void (callable::*f)(void))
{
	ticklist.remtick(c,f);
}
void gadget::RemoveTicker(void (*f)(void))
{
	ticklist.remtick(f);
}


// ----------------------------------------------------------------
// ----- select callback class ------------------------------------
// ----------------------------------------------------------------

void fdlist::addread(int fd,void *c,void* (*f)(int))
{
	fdnode *n=new fdnode;
	if (n)
	{
		n->fcb.set(c,(CBCAST) f);
		n->fd=fd;
		if (fd>maxfd) maxfd=fd;
		n->mode=fdnode::READFD;
		AddTail((node*)n);
		FD_SET(fd,&readfd);
	}
}
void fdlist::addread(int fd,callable *c,void (callable::*f)(int))
{
	fdnode *n=new fdnode;
	if (n)
	{
		n->fcb.set(c,(CALLFUNCTION) f);
		n->fd=fd;
		if (fd>maxfd) maxfd=fd;
		n->mode=fdnode::READFD;
		AddTail((node*)n);
		FD_SET(fd,&readfd);
	}
}
void fdlist::addwrite(int fd,void *c,void* (*f)(int))
{
	fdnode *n=new fdnode;
	if (n)
	{
		n->fcb.set(c,(CBCAST) f);
		n->fd=fd;
		if (fd>maxfd) maxfd=fd;
		n->mode=fdnode::WRITEFD;
		AddTail((node*)n);
		FD_SET(fd,&writefd);
	}
}
void fdlist::addwrite(int fd,callable *c,void (callable::*f)(int))
{
	fdnode *n=new fdnode;
	if (n)
	{
		n->fcb.set(c,(CALLFUNCTION) f);
		n->fd=fd;
		if (fd>maxfd) maxfd=fd;
		n->mode=fdnode::WRITEFD;
		AddTail((node*)n);
		FD_SET(fd,&writefd);
	}
}
void fdlist::addexcept(int fd,void *c,void* (*f)(int))
{
	fdnode *n=new fdnode;
	if (n)
	{
		n->fcb.set(c,(CBCAST) f);
		n->fd=fd;
		if (fd>maxfd) maxfd=fd;
		n->mode=fdnode::EXCEPTFD;
		AddTail((node*)n);
		FD_SET(fd,&exceptfd);
	}
}
void fdlist::addexcept(int fd,callable *c,void (callable::*f)(int))
{
	fdnode *n=new fdnode;
	if (n)
	{
		n->fcb.set(c,(CALLFUNCTION) f);
		n->fd=fd;
		if (fd>maxfd) maxfd=fd;
		n->mode=fdnode::EXCEPTFD;
		AddTail((node*)n);
		FD_SET(fd,&exceptfd);
	}
}
void fdlist::remfd(int fd)
{
	fdnode *n1=(fdnode*)head,*n2;
	while (n1->base.next)
	{
		n2=(fdnode*)n1->base.next;
		if (n1->fd==fd)
		{
			if (n1->mode==fdnode::READFD)
				FD_CLR(fd,&readfd);
			else if (n1->mode==fdnode::WRITEFD)
				FD_CLR(fd,&writefd);
			else if (n1->mode==fdnode::EXCEPTFD)
				FD_CLR(fd,&exceptfd);
			Remove((node*)n1);
			delete n1;
		}
		n1=n2;
	}
}
int fdlist::callback(fd_set *readset,fd_set *writeset,fd_set *exceptset)
{
	fdnode *n1=(fdnode*)head;

	while (n1->base.next)
	{
		n1->done=0;
		n1=(fdnode*)n1->base.next;
	}
	n1=(fdnode*)head;

	while (n1->base.next)
	{
		if (!n1->done && n1->mode==fdnode::READFD && FD_ISSET(n1->fd,readset))
		{
			n1->done=1;
			n1->fcb.call((void*)n1->fd);
			n1=(fdnode*)head;
		}
		else if (!n1->done && n1->mode==fdnode::WRITEFD && FD_ISSET(n1->fd,writeset))
		{
			n1->done=1;
			n1->fcb.call((void*)n1->fd);
			n1=(fdnode*)head;
		}
		else if (!n1->done && n1->mode==fdnode::EXCEPTFD && FD_ISSET(n1->fd,exceptset))
		{
			n1->done=1;
			n1->fcb.call((void*)n1->fd);
			n1=(fdnode*)head;
		}
		else
			n1=(fdnode*)n1->base.next;
	}
	return 0;
}

// ----------------------------------------------------------------
// ----- tick callback class ------------------------------------
// ----------------------------------------------------------------

void ticklist::addtick(callable *c,void (callable::*f)(void),int delay,enum ticknode::ticktype mode)
{
	ticknode *n=new ticknode;
	if (n)
	{
		n->fcb.set(c,(CALLFUNCTION) f);
		n->delay=delay;
		n->mode=mode;
		AddTail((node*)n);
	}
}
void ticklist::addtick(void (*f)(void),int delay,enum ticknode::ticktype mode)
{
	ticknode *n=new ticknode;
	if (n)
	{
		n->fcb.set((CBCAST) f);
		n->delay=delay;
		n->mode=mode;
		AddTail((node*)n);
	}
}
void ticklist::remtick(callable *c,void (callable::*f)(void))
{
	ticknode *n1=(ticknode*)head,*n2;
	while (n1->base.next)
	{
		n2=(ticknode*)n1->base.next;
		if (n1->fcb.compare(c,(CALLFUNCTION) f))
		{
			Remove((node*)n1);
			delete n1;
		}
		n1=n2;
	}
}
void ticklist::remtick(void (*f)(void))
{
	ticknode *n1=(ticknode*)head,*n2;
	while (n1->base.next)
	{
		n2=(ticknode*)n1->base.next;
		if (n1->fcb.compare((CBCAST) f))
		{
			Remove((node*)n1);
			delete n1;
		}
		n1=n2;
	}
}
int ticklist::callback(void)
{
	ticknode *n1=(ticknode*)head,*n2;

	while (n1->base.next)
	{
		if (++n1->counter>=n1->delay)
		{
			n1->fcb.call();
			if (n1->mode==ticknode::ONCE)
			{
				n1->mode=ticknode::TOREMOVE;
			}
			n1->counter=0;
		}
		n1=(ticknode*)n1->base.next;
	}

	n1=(ticknode*)head;
	while (n1->base.next)
	{
		n2=(ticknode*)n1->base.next;
		if (n1->mode==ticknode::TOREMOVE)
		{
			Remove((node*)n1);
			delete n1;
		}
		n1=n2;
	}

	return 0;
}

// ----------------------------------------------------------------
// ----- help bubbles ---------------------------------------------
// ----------------------------------------------------------------

//#define HELP_WAIT	(10* 3)	// bis Help geöffnet
//#define HELP_OPEN	(10* 4)	// Help offen
//#define HELP_FONT	"-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"

#define HELP_WAIT	defaults.help_wait
#define HELP_OPEN	defaults.help_shown
#define HELP_FONT	FONT_HELP

#ifdef SHAPE
#include <X11/extensions/shape.h>
#define PFEIL 		20
#define SCHATTEN	5

 static int do_shape=-1;
 static Pixmap shape_map;
 static GC shape_gc;
#else
#define SCHATTEN	0

 static int do_shape=0;
#endif

#define RAHMEN	2
int opentick=HELP_WAIT,helpopen=0,closetick=0;
int help_height,help_width,help_x,help_y;
class Xwindows helpwin;
class gfx_text helptxt;
class helpgadget helpgg;
// class gadget *realhelp;


helpgadget::helpgadget() : gadget(), Ddraw(this)
{
	text=NULL; pfeil=0; col=0;
	Name("helpgadget");
	helptxt.Name("helptext");
	helptxt.ParentClass(helpgg);
	helpgg.ParentClass(helpwin);
	helpwin.Name("helpwindow");

	dummy.ParentClass(helpwin);
	dummy.Name("helpdummy");
}

void pgadget::OpenHelp(int flag)
{ // flag == 0 : ticken oder öffnen
  //      == 1 : ticker=0

	if (HELP_WAIT==0) return;
	if (flag)
	{
		helpopen=0;
//		realhelp=NULL;
		if (helptext)
		{
			if (strlen(helptext))
			{
				helpgg.text=helptext;
			}
			else
			{
				currenthelp=NULL;
			}
		}
		else
		{ // anderes GG suchen mit Help
			ggs *g=FindGG(o->parent,GT_GADGET);
			if (g)
			{
				gadget *oldhelp=currenthelp;
				currenthelp=g->ggclasse;
				currenthelp->p->OpenHelp(2);
//				realhelp=currenthelp;
				if (currenthelp) currenthelp=oldhelp;
			}
			else
			{
				currenthelp=NULL;
			}
		}
	}
	else
	{
  		if (!helpopen && (--opentick<=0))
  		{
  			opentick=HELP_WAIT;
  			closetick=HELP_OPEN;
			// Fenster absolut zu X/Y über GGTools-Funktionen öffnen
			// kein Rahmen
			// um ca. 2 Punkte rechts/unten von Mauszeiger öffnen

			int x,y,w,h,mx,my,dummyi;
			Window dummyw;
			unsigned int dummyui;

			GetPositionOnRoot(o->display(),o->win,x,y);
			if (XQueryPointer(o->display(),o->win,&dummyw,&dummyw,&mx,&my,&dummyi,&dummyi,&dummyui))
			{
				help_x=0;
				help_y=0;
				help_height=0;
				help_width=0;
				if (!helpgg.col) helpgg.col=helpgg.AllocColor(0xfafa,0xfafa,0xc8c8);
				helpgg.Font(HELP_FONT);
				helpgg.GSetLimits();
				w=helpgg.gg_minwidth()+RAHMEN+RAHMEN;
				h=helpgg.gg_minheight()+RAHMEN+RAHMEN;
#ifdef SHAPE
				if (do_shape==-1)
				{
					int shape_event_base, shape_error_base;
					if (!defaults.help_bubblemode || !XShapeQueryExtension(o->display(),
						&shape_event_base,&shape_error_base))
					{
						if (defaults.help_bubblemode) warn_printf ("no shape extension on this display",&helpgg);
						do_shape = 0;
					}
					else
					{
						do_shape=1;
					}
				}
				if (do_shape)
				{
					help_height=PFEIL;
//					w+=8;
					h+=help_height;
				}
#endif
//				x+=o->width-9; 
				if (x<mx) x=mx+15;
				if (x<0) { helpgg.pfeil=-x; x=0; } else helpgg.pfeil=0;
//				y+=o->height-9;
				if (y<my) y=my+4;
				if (y<0) y=0;
				if (do_shape)
				{
					helpwin.Create(x,y,w+SCHATTEN,h);
				}
				else
					helpwin.Create(x,y,w,h);

				helpwin.OverrideRedirect();
				helpwin.Background(helpgg.col_shinebackground());
				helpwin.BorderWidth(0);
				helpwin.SaveUnder();
#ifdef SHAPE
				if (do_shape)
				{
					if (shape_map=XCreatePixmap(o->display(),o->rootwindow(),w+SCHATTEN,h,1))
					{
						if (shape_gc=XCreateGC(o->display(),shape_map,0,0))
						{
							int j;
							XSetForeground(o->display(),shape_gc,0);
							XFillRectangle(o->display(),shape_map,shape_gc,0,0,w+SCHATTEN,h);

							XSetForeground(o->display(),shape_gc,1);
							XFillRectangle(o->display(),shape_map,shape_gc,1,0,w-2,h-PFEIL);
							XFillRectangle(o->display(),shape_map,shape_gc,0,1,w,h-PFEIL-2);

							GetPositionOnRoot(o->display(),o->win,x,y);
							x=x+o->width-w-6;
							y=y-h+6; 
							if (x<0) { helpgg.pfeil=-x; x=0; } else helpgg.pfeil=0;
							if (y<0) y=0;
							if (my<(y+h)) y=my-2-h;

							j=4;
							for (int i=0;i<PFEIL;i++)
							{
								XDrawLine(o->display(),shape_map,shape_gc,w-helpgg.pfeil-PFEIL-j,h-PFEIL+i-1,w-helpgg.pfeil-i,h-PFEIL+i-1);
								if (j>0) j--;
							}

							{ // Schatten
				                char dot[]={1,1};
				                int cc=0,m;
        		                XSetDashes(o->display(),shape_gc,0,dot,2);
                				XSetLineAttributes(o->display(),shape_gc,0,LineOnOffDash,CapButt,JoinRound);

								for(int i=0;i<h-PFEIL;i++)
								{
	        		                XSetDashes(o->display(),shape_gc,cc,dot,2);
	        		                cc=1-cc;
									XDrawLine(o->display(),shape_map,shape_gc,SCHATTEN,i+SCHATTEN,w+SCHATTEN,i+SCHATTEN);
								}
								cc=1-cc;
								for (int i=0;i<PFEIL;i++)
								{
	        		                XSetDashes(o->display(),shape_gc,cc,dot,2);
	        		                cc=1-cc;
									m=((i+1)*SCHATTEN*2)/PFEIL;
									XDrawLine(o->display(),shape_map,shape_gc,w-helpgg.pfeil-PFEIL,SCHATTEN+h-PFEIL+i-1,SCHATTEN+w-helpgg.pfeil-i-m,SCHATTEN+h-PFEIL+i-1);
								}

				                XSetLineAttributes(o->display(),shape_gc,0,LineSolid,CapButt,JoinRound);
							}
							XShapeCombineMask(o->display(),helpwin.window(),ShapeBounding,
												0,0,shape_map,ShapeSet);
							helpwin.Move(x,y);
						}
						else
						{
							XFreePixmap(o->display(),shape_map);
							shape_map=0;
						}
					}
				}
				else
#endif
					helpwin.BorderWidth(1);
				helpgg.Dimensions(helpwin.window(),0,0,w+SCHATTEN,h);
				if (helpgg.Create())
				{
					helpgg.MCursor(XC_question_arrow);
					helpwin.Open();
					helpopen=1;
				}
			}
		}
		else
		{
			if (helpopen==1 && HELP_OPEN && --closetick<0)
			{
				CloseHelp(0);
				helpopen=2;
			}
		}
	}
}
void pgadget::CloseHelp(int flag)
{
	if (helpopen==1)
	{
		if (flag) opentick=6;
		helpgg.Free();
		helpwin.Destroy();
#ifdef SHAPE
				if (do_shape)
				{
					if (shape_map)
					{
						XFreeGC(o->display(),shape_gc);
						XFreePixmap(o->display(),shape_map);
						shape_map=0;
					}
				}
#endif
		helpopen=0;
	}
}

// subclass helpgadget

void helpgadget::GExpose(XEvent *event)
{ // Text ausgeben
	helptxt.draw_normal(this,RAHMEN+help_x,RAHMEN+help_y,width-RAHMEN-RAHMEN-help_width-(do_shape?SCHATTEN:0),height-RAHMEN-RAHMEN-help_height);
	XSetForeground(display(),gc,black);
	if (!do_shape)
	{
		XDrawLine(display(),win,gc,0,0,10,0);
		XDrawLine(display(),win,gc,0,1,10,1);
		XDrawLine(display(),win,gc,0,0,0,10);
		XDrawLine(display(),win,gc,1,0,1,10);
	}
#ifdef SHAPE
	else
	{
		int j;
		XDrawRectangle(display(),win,gc,0,0,width-SCHATTEN-1,height);
		j=4;
		for (int i=0;i<PFEIL+SCHATTEN;i++)
		{
			XDrawLine(display(),win,gc,0,height-PFEIL+i-1,width-SCHATTEN-PFEIL-j-pfeil,height-PFEIL+i-1);
			XDrawLine(display(),win,gc,width,height-PFEIL+i-1,width-SCHATTEN-i-pfeil,height-PFEIL+i-1);
			if (j>0) j--;
		}
		XFillRectangle(display(),win,gc,width-SCHATTEN,0,SCHATTEN,height);
	}
#endif
}
void helpgadget::GSetLimits(void)
{ // Text Höhe/Breite
	helptxt.NoUnderlineText(text);
	helptxt.Adjust(ADJUST_LEFT|ADJUST_TOP);
	minwidth=maxwidth=helptxt.width(this);
	minheight=maxheight=helptxt.height(this);
}
int helpgadget::Create(void)
{
	Background(col);
	if (gadget::Create())
	{
		Mode(GM_ONEEXPOSE);
		return 1;
	}
	return 0;
}

#endif
