#ifndef CODE_POPUP_CXX
#define CODE_POPUP_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: popup.cxx,v 1.10 2002/03/21 09:41:00 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 "types.h"
#include <ctype.h>
#include <Xclasses/gadget.h>
#include <Xclasses/popup.h>
#include "../include/defaults.h"
#include "../include/callback.h"
#include <Xclasses/callable.h>
#include "include/popup_p.h"
#include <Xclasses/tools.h>
#include <Xclasses/gfx_knob.h>
#include <Xclasses/gfx_arrow.h>
#include <Xclasses/gfx_checkbox.h>
#include <Xclasses/keyclass.h>

static gfx_knob popknob;
static gfx_checkbox popcheck;
static gfx_arrow poparrow(ARROW_RIGHT);

popup::popup() : gadget(), Ddraw(this)
{
	while (!(p=new ppopup))
	{
		outOfMemory("popup");
	}
	p->o=this;
	p->win.ParentClass(this);
	p->win.Name("popupwindow");
	p->subwin.ParentClass(this);
	p->subwin.Name("popupsubwindow");
	p->subgg.ParentClass(p->subwin);
	p->win.Name("popupsubgadget");
	Name("popup");
}
ppopup::ppopup() : callable(), win(), subwin(), subgg(), txt(), fcb()
{
	open=0;
	sopen=0;
	w=1;
	h=1;
	addto=NULL;
	head=NULL;
	userdata=0;
	kc=NULL;
	go_down=0;
	go_up=0;
	sel=0;
	subsel=0;
	oldsel=0;
	oldsubsel=0;
}
ppopup::~ppopup()
{
}
popup::~popup()
{
	if (win) Free();
	if (p) delete p;
}
void popup::KeyHook(int hook(popup *,int),int userdata)
{
	p->fcb.set((CBCAST) hook);
	p->userdata=userdata;
}
void popup::KeyHook(callable *hc,int (callable::*hook)(popup *,int),int userdata)
{
	p->fcb.set(hc,(CALLFUNCTION) hook);
	p->userdata=userdata;
}

void popup::Free(void)
{// MPOP: subgg/subwin array
	p->subgg.Free();
	gadget::Free();
	p->win.Destroy();
	p->subwin.Destroy();
	Clear();
}

int popup::Create(void)
{// MPOP: subgg/subwin array
	if (win) return 1;
	p->kc=GetKeyclass(ParentClass());
	p->win.Create(0,0,100,40);
	p->win.Background(col_background());
	p->win.OverrideRedirect();
	p->win.SaveUnder();
	p->win.BorderWidth(0);
	p->subwin.Create(0,0,10,10);
	p->subwin.Background(col_background());
	p->subwin.OverrideRedirect();
	p->subwin.SaveUnder();
	p->subwin.BorderWidth(0);

	Dimensions(p->win.window(),0,0,10,10);
	Background(col_background());
	BorderSize(2);
	p->subgg.Dimensions(p->subwin.window(),0,0,10,10);
	p->subgg.Background(col_background());
	p->subgg.CopyFont(this);
	p->subgg.SendEventTo(this);

    if (gadget::Create())
    {
        Mode(GM_BUTTON1|GM_BUTTON3|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ALLMOVES|GM_MINIMALMOVE);
        SetEvents(PointerMotionMask);
        MCursor(XC_arrow);
        // Tasten in kc eintragen
        p->setkeys(0,&p->txt);
        if (p->subgg.Create())
        {
        	return 1;
        }
        return 0;
    } 
    return 0;
}
void popup::Open(int w)
{
	int g=0;
	Window wdummy;
	int idummy;
	int xx,yy,rw,rh;

	p->h=p->calcpop(&p->txt);
	p->w=p->calcmax(&p->txt);
	if (p->w<w) p->w=w;
	p->win.Resize(p->w,p->h);
	gadget::Resize(p->w,p->h);
	gadget::Move(0,0);

	p->go_down=0;
	p->go_up=0;
	p->tohigh=0;
	p->tohighstart=0;

	DisplaySize(display(),rw,rh);
	if (p->y+p->h>rh)
	{
//		p->y=rh-p->h;
//		g=1;
		p->h=rh-p->y-10;
		p->win.Resize(p->w,p->h);
		p->tohigh=1;
	}
	if (p->x+p->w>rw)
	{
		p->x=rw-p->w;
		g=1;
	}
	if (g) p->win.Move(p->x,p->y);
	p->sel=0;
	p->subsel=0;// MPOP: array

	XQueryPointer(display(),p->win.window(),&wdummy,&wdummy,&p->oldx,&p->oldy,&xx,&yy,(unsigned int*)&idummy);
	p->select(xx,yy);
	p->paint();
	p->win.Open();
	p->open=1;

	Mode(GM_BUTTON1|GM_BUTTON3|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ALLMOVES|GM_MINIMALMOVE|GM_ACTIVETICK|GM_NORMALTICK);

	GrabPointer(False,
					ButtonPressMask|
					ButtonReleaseMask|
					ButtonMotionMask|
					(defaults.nopopupmove?0:PointerMotionMask),
					GrabModeAsync,
					GrabModeAsync,
					None,
					None); //XC_arrow,

// #ifndef DEBUG
// 	g=XGrabPointer(display(),
// 					win,
// 					False,
// 					ButtonPressMask|
// 					ButtonReleaseMask|
// 					ButtonMotionMask|
// 					(defaults.nopopupmove?0:PointerMotionMask),
// 					GrabModeAsync,
// 					GrabModeAsync,
// 					None,
// 					None, //XC_arrow,
// 					CurrentTime);
// #endif
// 	if (g==GrabSuccess) p->grabed=1; else p->grabed=0;
// 	IgnoreExcept(0);
}
void popup::Open(int ox,int oy,int w)
{
	p->win.Move(ox,oy);
	p->x=ox;
	p->y=oy;
	Open(w);
}
void popup::OpenAt(int ox,int oy,int sel,int w)
{
	zeilen *zz;
	int i=0;

	p->h=p->calcpop(&p->txt);
	p->w=p->calcmax(&p->txt);

	zz=(zeilen*)p->txt.head;
	while (zz->base.next)
	{
		if (sel==i)
		{
			if (oy<0) oy=0;
			p->win.Move(ox,oy);
			p->x=ox;
			p->y=oy;
		}
		oy-=zz->h;
		i++;
		zz=(zeilen*)zz->base.next;
	}
	
	Open(w);
	p->select2();
}
void popup::Close(void)
{
//	IgnoreNothing();
//	if (p->grabed) XUngrabPointer(display(),CurrentTime);

	UngrabPointer();
	Mode(GM_BUTTON1|GM_BUTTON3|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ALLMOVES|GM_MINIMALMOVE);
	if (p->sopen)
	{
		p->subwin.Close();// MPOP: array
		p->sopen=0;// MPOP: array?
	}
	p->win.CloseU();
	p->open=0;
}

int popup::PopupOpen(void)
{
	return p->open;
}

void popup::Resize(int w,int h)
{
}
void popup::Lock(void)
{
	gadget::Lock();
}
void popup::Unlock(void)
{
	gadget::Unlock();
}
void popup::Block(int b)
{
	gadget::Block(b);
	p->subgg.Block(b);// MPOP: array
}
void popup::Unblock(int b)
{
	p->subgg.Unblock(b);// MPOP: array
	gadget::Unblock(b);
}

zeilen *ppopup::findID(int id)
{
	zeilen *z1;
	head=NULL;

	if (id>0)
	{
		z1=(zeilen*)txt.head;
		while (z1->base.next)
		{
			if (z1->nr==id)
			{
				return z1;
			}
			else
			{
				if (z1->sub)
				{ // MPOP: das rekursiv für sub-sub-sub popups
					zeilen *z2=(zeilen*)z1->subzeilen.head;
					while (z2->base.next)
					{
						if (z2->nr==id)
						{
							head=z1;
							return z2;
						}
						z2=(zeilen*)z2->base.next;
					}
				}
			}
			z1=(zeilen*)z1->base.next;
		}
	}
	return NULL;
}

void popup::LockItem(int id)
{
	zeilen *z1=p->findID(id);

	if (z1)
	{
		z1->locked=1;
	}
	if (p->open) GExpose(NULL);
}
void popup::UnlockItem(int id)
{
	zeilen *z1=p->findID(id);
	if (z1)
	{
		z1->locked=0;
	}
	if (p->open) GExpose(NULL);
}
void popup::LockHeadOf(int id)
{
	zeilen *z1=p->findID(id);

	if (z1 && p->head)
	{
		p->head->locked=1;
	}
	if (p->open) GExpose(NULL);
}
void popup::UnlockHeadOf(int id)
{
	zeilen *z1=p->findID(id);
	if (z1 && p->head)
	{
		p->head->locked=0;
	}
	if (p->open) GExpose(NULL);
}

void popup::State(int id,int state)
{
	zeilen *z1=p->findID(id);
	if (z1)
	{
		z1->pressed=state==PRESSED;
	}
	if (p->open) GExpose(NULL);
}
int popup::State(int id)
{
	int r=RELEASED;
	zeilen *z1=p->findID(id);
	if (z1)
	{
		r=z1->pressed?PRESSED:RELEASED;
	}
	return r;
}

void popup::Selected(int id,int s)
{
	zeilen *z1=p->findID(id);
	if (z1)
	{
		zeilen *z2=z1;
		int i=0;
//		while (z2->base.next && ((zeilen*)z2->base.next)->mx && ((zeilen*)z2->base.next)->nr==z1->nr)
		while (z2->base.next && z2->mx && z2->nr==z1->nr)
		{
			if (i==s) z2->pressed=1; else z2->pressed=0;
			i++;
			z2=(zeilen*)z2->base.next;
		}
	}
}
int popup::Selected(int id)
{
	int r=0;
	zeilen *z1=p->findID(id);
	if (z1)
	{ 
		zeilen *z2=z1;
		int i=0;
//		while (z2->base.next && ((zeilen*)z2->base.next)->mx && ((zeilen*)z2->base.next)->nr==z1->nr)
		while (z2->base.next && z2->mx && z2->nr==z1->nr)
		{
			if (z2->pressed) r=i;
			i++;
			z2=(zeilen*)z2->base.next;
		}
	}
	return r;
}

zeilen *ppopup::Add(char *text,char *scut)
{
	zeilen *z=new zeilen;
	char *scutres=NULL;
	Xclasses *oo=o;

	if (addto)
	{
		oo=&addto->text;
	}
	z->text.ParentClass(oo);
	z->scut.ParentClass(oo);

	z->locked=0;
	z->sub=0;
	z->select=0;
	z->check=0;
	z->mx=0;
	z->pressed=0;
	z->ruler=0;
	z->head=0;
	z->nr=0;
	z->issub=0;
	z->subboss=NULL;
//	z->text.Text(text);
	z->text.NoUnderlineText(text);
	z->text.Name(text);
	z->text.Adjust(ADJUST_LEFT|ADJUST_MIDDLE);
	z->scut.Name("scut");
	if (scutres=z->text.GetResource("shortcut"))
	{
		scut=scutres;
	}
	if (scut)
	{
		strncpy(z->scutC,scut,sizeof(z->scutC)-1);
		z->hascut=1;
		z->scut.Adjust(ADJUST_RIGHT|ADJUST_MIDDLE);
	}
	else
	{
		z->scutC[0]=0;
//		z->scut.Text(NULL);
		z->hascut=0;
	}
	z->gfx=NULL;
	return z;
}

void popup::Add(char *text,char *scut,int id)
{
	zeilen *z=p->Add(text,scut);
	z->select=1;
	if (p->addto)
	{
		p->addto->subzeilen.AddTail((node*)z);
		z->nr=id;
		z->issub=1;
		z->subboss=p->addto;
	}
	else
	{
		p->txt.AddTail((node*)z);
		z->nr=id;
		z->issub=0;
	}
}
void popup::AddHeadline(char *text)
{
	zeilen *z=p->Add(text,NULL);
	z->head=1;
	z->locked=1;
	z->text.Adjust(ADJUST_MIDDLE|ADJUST_CENTER);
	if (p->addto)
	{
		p->addto->subzeilen.AddTail((node*)z);
		z->issub=1;
		z->subboss=p->addto;
	}
	else
	{
		p->txt.AddTail((node*)z);
		z->issub=0;
	}
}
void popup::AddRuler(void)
{
	zeilen *z=p->Add(NULL,NULL);
	z->ruler=1;
	z->locked=1;
	if (p->addto)
	{
		p->addto->subzeilen.AddTail((node*)z);
		z->issub=1;
		z->subboss=p->addto;
	}
	else
	{
		p->txt.AddTail((node*)z);
		z->issub=0;
	}
}
void popup::AddSel(char *text,char *scut,int id)
{
	zeilen *z=p->Add(text,scut);
	z->check=1;
	z->gfx=&popcheck;
	if (p->addto)
	{
		p->addto->subzeilen.AddTail((node*)z);
		z->nr=id;
		z->issub=1;
		z->subboss=p->addto;
	}
	else
	{
		p->txt.AddTail((node*)z);
		z->nr=id;
		z->issub=0;
	}
}
void popup::AddMX(char **text,char **scut,int id)
{
	int c=0;
	char **sc=scut;
	while (*text)
	{
		zeilen *z;
		if (scut)
			z=p->Add(*text,*sc);
		else
			z=p->Add(*text,NULL);
		z->mx=1;
		z->gfx=&popknob;
		if (c==0) z->pressed=1;
		if (p->addto)
		{
			p->addto->subzeilen.AddTail((node*)z);
			z->nr=id;
			z->issub=1;
			z->subboss=p->addto;
		}
		else
		{
			p->txt.AddTail((node*)z);
			z->nr=id;
			z->issub=0;
		}
		c++;
		text++;
		if (scut) sc++;
	}
}
void popup::AddSub(char *text)
{
	zeilen *z=p->Add(text,NULL);
	z->sub=1;
	p->txt.AddTail((node*)z);
	p->addto=z;// MPOP: alte Ebene merken
}
void popup::EndSub(void)
{
	p->addto=NULL;// MPOP: eine ebene höher
}
void popup::Clear(void)
{
	zeilen *z1,*z2;
	p->addto=NULL;

	p->setkeys(1,&p->txt);
	while (z1=(zeilen*)p->txt.RemTail())
	{
		if (z1->sub)
		{// MPOP: rekursiv
			while (z2=(zeilen*)z1->subzeilen.RemTail())
			{
				delete z2;
			}
		}
		delete z1;
	}
}
int popup::PID(void)
{
	int r=0;
	zeilen *z1=(zeilen*)p->txt.Find(p->sel);
	if (z1)
	{
		if (z1->sub)
		{// MPOP: rekursiv
			zeilen *z2=(zeilen*)z1->subzeilen.Find(p->subsel);
			if (z2) r=z2->nr;
		}
		else
		{
			r=z1->nr;
		}
	}
	return r;
}
void ppopup::setkeys(int mode,dlist *z)
{ // mode: 0: add key, 1: del key
// Tasten in kc eintragen

	zeilen *zz;
	char txt[30];
	int c,q;
	char cc[2];

	zz=(zeilen*)z->head;
	while (zz->base.next)
	{
		if (zz->hascut && strlen(zz->scutC)>0)
		{
			q=0;
			c=0;
			cc[0]=0; cc[1]=0;
			if (strlen(zz->scutC)==1)
			{
				if (isupper((int)zz->scutC[0]))
				{
					sprintf(txt,"Shift+Ctrl+%c",(int)zz->scutC[0]);
					q=KEY_CONTROL|KEY_SHIFT;
				}
				else
				{
					sprintf(txt,"Ctrl+%c",toupper((int)zz->scutC[0]));
					q=KEY_CONTROL;
				}
				cc[0]=zz->scutC[0];
			}
			else if (strlen(zz->scutC)==2 && toupper((int)zz->scutC[0])=='A')
			{ // Alt "Ax"
				if (isupper((int)zz->scutC[1]))
				{
					sprintf(txt,"Shift+Alt+%c",(int)zz->scutC[1]);
					q=KEY_ALT|KEY_SHIFT;
				}
				else
				{
					sprintf(txt,"Alt+%c",toupper((int)zz->scutC[1]));
					q=KEY_ALT;
				}
				cc[0]=zz->scutC[1];
			}
			else if (strlen(zz->scutC))
			{
				if (toupper(zz->scutC[0])=='F')
				{ // F-Tasten
					int fk;
					if (fk=atoi(&zz->scutC[1]))
					{
						c=XK_F1-1+fk;
						if (zz->scutC[0]=='f')
						{
							strncpy(txt,zz->scutC,sizeof(txt));
							txt[0]='F';
						}
						else
						{
							sprintf(txt,"Shift+%s",zz->scutC);
							q=KEY_SHIFT;
						}
					}
				}
				else if (strcasecmp(zz->scutC,"ESC")==0)
				{ // ESC
					c=XK_Escape;
					if (islower(zz->scutC[0]))
					{
						strcpy(txt,"Esc");
					}
					else
					{
						strcpy(txt,"Shift+Esc");
						q=KEY_SHIFT;
					}
				}
				else if (strcasecmp(zz->scutC,"DEL")==0)
				{ // Del
					c=XK_Delete;
					if (islower(zz->scutC[0]))
					{
						strcpy(txt,"DEL");
					}
					else
					{
						strcpy(txt,"Shift+DEL");
						q=KEY_SHIFT;
					}
				}
				else
				{
					strncpy(txt,zz->scutC,sizeof(txt));
					c=XK_F12;
				}
			}
			if (mode==0)
			{
				zz->scut.Text(txt);
				if (kc)
				{ // Short Cuts
					if (c)
						kc->SendHookSelectTo(c,q,o,this,(SENDHOOK)&ppopup::Hook,(int)zz);
					else if (cc)
						kc->SendHookSelectTo(cc,q,o,this,(SENDHOOK)&ppopup::Hook,(int)zz);
				}
			}
			else if (mode==1)
			{
				if (kc)
				{
					if (c)
						kc->FreeKey(c,q);
					else
						kc->FreeKey(cc,q);
				}
			}
		}
		if (zz->sub)
		{
			setkeys(mode,&zz->subzeilen);
		}
		zz=(zeilen*)zz->base.next;
	}
}
int ppopup::Hook(XEvent *event,KeySym xk,char *key,int userd)
{
	if (open) return 0;

	int r=0;
	zeilen *zz,*z2;
	
	z2=(zeilen*)userd;
	if (z2->issub)
	{
		zz=z2->subboss;
	}
	else
	{
		zz=z2;
	}

	if (zz->locked || z2->locked) return 0;
	if (z2->check)
	{
		z2->pressed=!z2->pressed;
	}
	else if (z2->mx)
	{
		if (z2->pressed) return 0;

		zeilen *z3=z2;
		while (z3->base.prev && ((zeilen*)z3->base.prev)->mx && ((zeilen*)z3->base.prev)->nr==z2->nr)
		{
			z3=(zeilen*)z3->base.prev;
		}
		while (z3->base.next && z3->mx && z3->nr==z2->nr)
		{
			z3->pressed=0;
			z3=(zeilen*)z3->base.next;
		}
		z2->pressed=1;
	}

	if (z2->issub)
	{
		zz=z2->subboss;
		subsel=zz->subzeilen.Find((node*)z2);
	}
	else
	{
		zz=z2;
		subsel=0;
	}
	sel=txt.Find((node*)zz);

	if (fcb.iscallback())
	{
		r=(int)fcb.call((void*)o,(void*)userdata);
	}
	else
	{
		r=1;
	}
	return r;
}
void ppopup::subopen(zeilen *z)
{
	int rw,rh;

	sh=calcpop(&z->subzeilen);
	sw=calcmax(&z->subzeilen);
	subwin.Resize(sw,sh);// MPOP: array
	subgg.Resize(sw,sh);// MPOP: array
	subgg.Move(0,0);// MPOP: array

	sx=x+w-z->h2-o->RealSize()*2;// MPOP: array
	sy=y+z->y-tohighstart;// MPOP: array

	go_down=0;
	go_up=0;
	subtohigh=0;// MPOP: array
	subtohighstart=0;// MPOP: array

	DisplaySize(o->display(),rw,rh);
	if (sy+sh>rh)
	{
//		sy=rh-sh;

		sh=rh-sy-10;// MPOP: array
		subwin.Resize(sw,sh);// MPOP: array
		subtohigh=1;// MPOP: array
	}
	if (sx+sw>rw)// MPOP: array
	{
		sx=x-sw+z->h2+o->RealSize()*2;// MPOP: array
	}
	if (sx<0)// MPOP: array
	{
		sx=rw-sw;// MPOP: array
	}
	subwin.Move(sx,sy);// MPOP: array

	subwin.Open();// MPOP: array
	sopen=1;// MPOP: array
}

int ppopup::calcpop(dlist *z)
{
	zeilen *zz;
	int y=TOP,h1,h2;

	zz=(zeilen*)z->head;
	while (zz->base.next)
	{
		if (zz->ruler)
		{
			zz->w=1;
			zz->h2=2-EXTRA-EXTRA+RULEREXTRA*2;
		}
		else
		{
			zz->w=zz->text.width(o);
			h1=zz->text.height(o);
			if (zz->hascut)
			{
				h2=zz->scut.height(o);
				zz->w+=zz->scut.width(o)+SCUTSPACE;
			}
			else
			{
				h2=0;
			}
			zz->h2=(h1>h2?h1:h2);
		}
		zz->h=zz->h2+EXTRA+EXTRA;

		char *gfxores;
		gfx_object *gfxo=zz->gfx;
		if (gfxores=zz->text.GetResource("graphic"))
		{
			gfxo=(gfx_object*)FindObject(gfxores);
		}
		if (gfxo)
		{
			if (gfxo->width(o)==NOLIMIT)
				zz->w+=zz->h2+2;
			else
				zz->w+=gfxo->width(o);
		}
		else if (defaults.popupline) zz->w+=zz->h2+2;
		if (zz->sub) zz->w+=zz->h2+20;
		zz->y=y;
		y+=zz->h;
		zz=(zeilen*)zz->base.next;
	}
	return y+TOP;
}
int ppopup::calcmax(dlist *z)
{
	zeilen *zz;
	int w=0;

	zz=(zeilen*)z->head;
	while (zz->base.next)
	{
		if (zz->w>w) w=zz->w;
		zz=(zeilen*)zz->base.next;
	}
	return w+SEITE*2+o->RealSize()*4+1+2;
}
int ppopup::select(int mx,int my)
{
	// B0: Auswahl, B1: Aktiv bleiben
	// 0: keine Auswahl
	// 1: keine Auswahl, Sub öffnen
	// 2: keine Auswahl
	// 3: Auswahl
	int cx,cy;
	zeilen *z1,*z2;

	oldsel=sel;
	oldsubsel=subsel;
	mx+=x;
	my+=y;

	if (sopen)
	{// MPOP: ?????
		if ((z1=(zeilen*)txt.Find(sel)))
		{
			if (!z1->locked)
			{
				z2=(zeilen*)z1->subzeilen.head;
				cx=mx-sx;
				cy=my-sy+subtohighstart-tohighstart;
				while (z2->base.next)
				{
					if (cx>SEITE && cx<sw-SEITE && 
					    cy>=z2->y && cy<=z2->y+z2->h)
					{
						if (z2->locked)
						{
							return 0;
						}
						else
						{
							subsel=z1->subzeilen.Find((node*)z2);
							return 3;
						}
					}
					z2=(zeilen*)z2->base.next;
				}
				subsel=0;
			}
		}
		else
		{
			return 0;
		}
	}

	z1=(zeilen*)txt.head;
	cx=mx-x;
	cy=my-y; //+tohighstart;
	while (z1->base.next)
	{
		if (cx>SEITE && cx<w-SEITE && 
		    cy>=z1->y && cy<=z1->y+z1->h)
		{
			if (z1->locked && !z1->sub)
			{
				return 0;
			}
			else
			{
				sel=txt.Find((node*)z1);
				select2();
				return (z1->sub?1:3); // 1 + 2(sub)
			}
		}
		z1=(zeilen*)z1->base.next;
	}
	if (!sopen)
	{
		sel=0;
	}
	return 2; // 0 + 2
}
void ppopup::select2(void)
{
	zeilen *z1=(zeilen*)txt.Find(sel);
	if (sel!=oldsel)
	{// MPOP: ????
		if (sopen)
		{
			subwin.Close();
			sopen=0;
			subsel=0;
		}
		if (z1->sub)
		{
			subopen(z1);
		}
	}
}
void ppopup::paint(void)
{
	if (o->win)
	{// MPOP: ????
		if (sel!=oldsel)
		{
			if (oldsel>0) zeile((zeilen*)txt.Find(oldsel));
			if (sel>0) zeile((zeilen*)txt.Find(sel));
		}
		if (sopen)
		{
			subwin.Raise();
			if (subsel!=oldsubsel)
			{
				if (oldsubsel>0) subzeile((zeilen*)((zeilen*)txt.Find(sel))->subzeilen.Find(oldsubsel));
				if (subsel>0) subzeile((zeilen*)((zeilen*)txt.Find(sel))->subzeilen.Find(subsel));
			}
		}
		else
		{
			win.Raise();
		}
	}
}
void ppopup::zeileaus(zeilen *z,int issel,gadget *g,int lockall)
{
	int rs=o->RealSize();
	if (defaults.popuplight)
		o->BorderSize(1);
	int pl=0;
	gfx_object *gfxo=z->gfx;
	char *gfxores;

	if (gfxores=z->text.GetResource("graphic"))
	{
		gfxo=(gfx_object*)FindObject(gfxores);
	}
	if (gfxo) pl=z->h2+2;
	if (defaults.popupline) pl=z->h2+2;
	if (z->head) pl=0;

	if (issel)
	{
		if (z->locked)
		{
			z->text.draw_locked(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
			z->scut.draw_locked(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
			if (z->sub) poparrow.draw_locked(g,g->gg_width()-z->h2-rs-o->RealSize()-2,z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
		}
		else
		{
			o->borderDownFill(g,SEITE+rs,z->y,g->gg_width()-(SEITE+rs)*2,z->h);
			if (defaults.popuplight)
			{
				z->text.draw_normal(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				z->scut.draw_normal(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				if (z->sub) poparrow.draw_normal(g,g->gg_width()-z->h2-rs-o->RealSize()-2,z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
			}
			else
			{			
				z->text.draw_selected(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				z->scut.draw_selected(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				if (z->sub) poparrow.draw_selected(g,g->gg_width()-z->h2-rs-o->RealSize()-2,z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
			}
		}
		if (gfxo)
		{
			if (z->pressed)
				gfxo->draw_selected(g,SEITE+1+rs+o->RealSize(),z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
			else
				gfxo->draw_normal(g,SEITE+1+rs+o->RealSize(),z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
		}
	}
	else
	{
		if (z->ruler)
		{
			XSetForeground(o->display(),g->gg_gc(),o->col_shadow2());
			XDrawLine(o->display(),g->gg_win(),g->gg_gc(),rs,z->y+RULEREXTRA,g->gg_width()-rs,z->y+RULEREXTRA);
			XSetForeground(o->display(),g->gg_gc(),o->col_shine2());
			XDrawLine(o->display(),g->gg_win(),g->gg_gc(),o->RealSize(),z->y+RULEREXTRA+1,g->gg_width()-o->RealSize(),z->y+RULEREXTRA+1);
		}
		else
		{
//			if (z->head)
//				XSetForeground(o->display(),g->gg_gc(),o->col_shinebackground());
//			else
				XSetForeground(o->display(),g->gg_gc(),o->col_background());
			XFillRectangle(o->display(),g->gg_win(),g->gg_gc(),SEITE+rs,z->y,g->gg_width()-(SEITE+rs)*2,z->h);
			if ((z->locked || lockall) && !z->head)
			{
				z->text.draw_locked(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				z->scut.draw_locked(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				if (z->sub) poparrow.draw_locked(g,g->gg_width()-z->h2-rs-o->RealSize()-2,z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
			}
			else
			{
				z->text.draw_normal(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				z->scut.draw_normal(g,pl+SEITE+1+rs+o->RealSize(),z->y,g->gg_width()-(SEITE+1+rs+o->RealSize())*2-pl,z->h);
				if (z->sub) poparrow.draw_normal(g,g->gg_width()-z->h2-rs-o->RealSize()-2,z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
				if (z->head)
				{
					long unsigned int pos;
					if (!XGetFontProperty(g->gg_font(),XA_UNDERLINE_POSITION,&pos)) pos=3;
					XSetForeground(o->display(),g->gg_gc(),o->col_shine2());
					XDrawLine(o->display(),g->gg_win(),g->gg_gc(),pl+SEITE+1+rs,z->y+z->h2+pos,g->gg_width()-SEITE-1-rs-pl,z->y+z->h2+pos);
					XSetForeground(o->display(),g->gg_gc(),o->col_shadow2());
					XDrawLine(o->display(),g->gg_win(),g->gg_gc(),pl+SEITE+1+rs,z->y+z->h2+pos+1,g->gg_width()-SEITE-1-rs-pl,z->y+z->h2+pos+1);
				}
			}
			if (gfxo)
			{
				if (z->pressed)
					if (z->locked || lockall)
						gfxo->draw_selectedlocked(g,SEITE+1+rs+o->RealSize(),z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
					else
						gfxo->draw_selected(g,SEITE+1+rs+o->RealSize(),z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
				else
					if (z->locked || lockall)
						gfxo->draw_locked(g,SEITE+1+rs+o->RealSize(),z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
					else
						gfxo->draw_normal(g,SEITE+1+rs+o->RealSize(),z->y+EXTRA,z->h2-IMGMINUS,z->h2-IMGMINUS);
			}
		}
	}
	if (defaults.popuplight)
		o->BorderSize(2);
}
void ppopup::zeile(zeilen *z)
{
// 	if (tohigh)
// 	{
// 		if (z->y<tohighstart)
// 		{
// 			return;
// 		}
// 		if (z->y+z->h>h+tohighstart)
// 		{
// 			return;
// 		}
// 	}
	if (z)
		zeileaus(z,sel==txt.Find((node*)z),o);
}
void ppopup::subzeile(zeilen *z)
{// MPOP: ????
// 	if (subtohigh)
// 	{
// 		if (z->y<subtohighstart)
// 		{
// 			return;
// 		}
// 		if (z->y+z->h>sh+subtohighstart)
// 		{
// 			return;
// 		}
// 	}
	if (z)
		zeileaus(z,subsel==((zeilen*)txt.Find(sel))->subzeilen.Find((node*)z),&subgg,((zeilen*)txt.Find(sel))->locked);
}

void popup::GExpose(XEvent *event)
{
	zeilen *zz;

	if (!wasfor || event==NULL)
	{
		borderUp(this,0,0,width,height);
		zz=(zeilen*)p->txt.head;
		while (zz->base.next)
		{
			p->zeile(zz);
			zz=(zeilen*)zz->base.next;
		}
	}
	if (p->sopen)
	{// MPOP: ????
		borderUp(&p->subgg,0,0,p->subgg.gg_width(),p->subgg.gg_height());
		zz=(zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.head;
		while (zz->base.next)
		{
			p->subzeile(zz);
			zz=(zeilen*)zz->base.next;
		}
	}
}
void popup::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
}
void popup::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
}
int popup::GActivate(XEvent *event,int why)
{
	int m=0;
	if (why==GA_KEY)
	{
		int c;
		char taste[5];
		KeySym key;

		p->oldsel=p->sel;
		p->oldsubsel=p->subsel;// MPOP: array

		c=XLookupString((XKeyEvent*)event,taste,sizeof(taste),&key,0);
		switch (key)
		{
			case XK_Up:
				if (p->txt.Count()<1) return 0;

				p->go_down=0;
				p->go_up=0;

				if (p->sopen && p->subsel>0)
				{// MPOP: array ????
					do
					{
						p->subsel--;
						if (p->subsel<1) p->subsel=1;
						if (p->subtohigh)
						{
							if (((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->y<p->subtohighstart)
							{
								p->subtohighstart=((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->y-5;
								if (p->subtohighstart<0)
								{
									p->subtohighstart=0;
								}
								p->subgg.Move(0,-p->subtohighstart);
							}
						}
					}
					while (((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->locked && p->subsel!=0);
					if (((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->locked) p->subsel=p->oldsubsel;
				}
				else
				{
					do
					{
						p->sel--;
						if (p->sel<1) p->sel=1;

						if (((zeilen*)p->txt.Find(p->sel))->y<p->tohighstart)
						{
							p->tohighstart=((zeilen*)p->txt.Find(p->sel))->y-5;
							if (p->tohighstart<0)
							{
								p->tohighstart=0;
							}
							gadget::Move(0,-p->tohighstart);
						}
					}
					while (((zeilen*)p->txt.Find(p->sel))->locked && p->sel!=0);
					if (((zeilen*)p->txt.Find(p->sel))->locked) p->sel=p->oldsel;
					p->select2();
				}
				m=GAR_MESSAGE;
				break;
			case XK_Down:
				if (p->txt.Count()<1) return 0;

				p->go_down=0;
				p->go_up=0;

				if (p->sopen && p->subsel>0)
				{// MPOP: array ????
					do
					{
						p->subsel++;
						if (p->subsel>((zeilen*)p->txt.Find(p->sel))->subzeilen.Count()) p->subsel=((zeilen*)p->txt.Find(p->sel))->subzeilen.Count();
						if (p->subtohigh)
						{
							if (((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->y+
								((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->h>
								p->sh+p->subtohighstart)
							{
								p->subtohighstart=((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->y;
								if (p->subgg.gg_height()-p->subtohighstart<p->sh)
								{
									p->subtohighstart=p->subgg.gg_height()-p->sh;
								}
								p->subgg.Move(0,-p->subtohighstart);
							}
						}
					}
					while (((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->locked && p->subsel!=((zeilen*)p->txt.Find(p->sel))->subzeilen.Count());
					if (((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel))->locked) p->subsel=p->oldsubsel;
				}
				else
				{
					do
					{
						p->sel++;
						if (p->sel>p->txt.Count()) p->sel=p->txt.Count();

						if (p->tohigh)
						{
							if (((zeilen*)p->txt.Find(p->sel))->y+
								((zeilen*)p->txt.Find(p->sel))->h>
								p->h+p->tohighstart)
							{
								p->tohighstart=((zeilen*)p->txt.Find(p->sel))->y;
								if (gadget::gg_height()-p->tohighstart<p->h)
								{
									p->tohighstart=gg_height()-p->h;
								}
								gadget::Move(0,-p->tohighstart);
							}
						}
					}
					while (((zeilen*)p->txt.Find(p->sel))->locked && p->sel!=p->txt.Count());
					if (((zeilen*)p->txt.Find(p->sel))->locked) p->sel=p->oldsel;
					p->select2();
				}
				m=GAR_MESSAGE;
				break;
			case XK_Right:
				if (p->txt.Count()<1) return 0;

				p->go_down=0;
				p->go_up=0;

				if (p->sopen && p->subsel==0 && !((zeilen*)p->txt.Find(p->sel))->locked)
				{// MPOP: array
					p->subsel=1;
					m=GAR_MESSAGE;
				}
				break;
			case XK_Left:
				if (p->txt.Count()<1) return 0;

				p->go_down=0;
				p->go_up=0;

				if (p->sopen && p->subsel>0)
				{// MPOP: array
					p->subsel=0;
					m=GAR_MESSAGE;
				}
				break;
			case XK_Escape:
				p->go_down=0;
				p->go_up=0;

				if (p->sopen && p->subsel>0)
				{// MPOP: array
					p->subsel=0;
				}
				else
				{
					p->sel=0;
					m=GAR_ACTIVE;
				}
				break;
			case XK_Return:
			case XK_KP_Enter:
				p->go_down=0;
				p->go_up=0;

				if ((p->sopen && p->subsel>0) || (!p->sopen && p->sel>0)) m=GAR_ACTIVE;// MPOP: array
				break;
		}
	}
	else if (why==GA_TICK)
	{
		int wsel=p->sel,wssel=p->subsel;// MPOP: array
		if (!p->sopen)
		{
			if (p->tohigh)
			{
				wsel=p->sel;
				if (wsel<1) wsel=1;
				if (p->go_down)
				{
					p->tohighstart+=((zeilen*)p->txt.Find(wsel))->h;
					if (gg_height()-p->tohighstart<p->h)
					{
						p->tohighstart=gg_height()-p->h;
					}
					gadget::Move(0,-p->tohighstart);
				}
				if (p->go_up)
				{
					p->tohighstart-=((zeilen*)p->txt.Find(wsel))->h;
					if (p->tohighstart<0)
					{
						p->tohighstart=0;
					}
					gadget::Move(0,-p->tohighstart);
				}
			}
		}
		else if (p->subtohigh)
		{// MPOP: array ????
			if (wsel<1) wsel=1;
			if (wssel<1) wssel=1;
			if (p->go_down)
			{
				p->subtohighstart+=((zeilen*)((zeilen*)p->txt.Find(wsel))->subzeilen.Find(wssel))->h;
				if (p->subgg.gg_height()-p->subtohighstart<p->sh)
				{
					p->subtohighstart=p->subgg.gg_height()-p->sh;
				}
				p->subgg.Move(0,-p->subtohighstart);
			}
			if (p->go_up)
			{
				p->subtohighstart-=((zeilen*)((zeilen*)p->txt.Find(wsel))->subzeilen.Find(wssel))->h;
				if (p->subtohighstart<0)
				{
					p->subtohighstart=0;
				}
				p->subgg.Move(0,-p->subtohighstart);
			}
		}
	}
	else if (event && (why==GA_BUTTONPRESS || why==GA_EVENT))
	{
		m=(p->select(event->xbutton.x,event->xbutton.y)& 2)?GAR_ACTIVE|GAR_MESSAGE:0;
	}
	p->paint();
	return m;
}
int popup::GSelected(XEvent *event,int why,int wasabove)
{
	int m=0;
	if (why==GA_BUTTONPRESS || why==GA_EVENT || why==GA_KEY)
	{
		if (event && why!=GA_KEY && event->xbutton.x_root==p->oldx && event->xbutton.y_root==p->oldy)
		{
			p->oldx=-1;
			p->oldy=-1;
			return 0;
		}
		if (why==GA_KEY)
		{
			m=(p->sel>0)?GSR_SELECTION|GSR_MESSAGE:0;
		}
		else if (event)
		{
			m=(p->select(event->xbutton.x,event->xbutton.y)&(~2))?GSR_SELECTION|GSR_MESSAGE:0;
		}
		if (m)
		{
			zeilen *z;
			if(p->subsel)
			{// MPOP: array 
				z=((zeilen*)((zeilen*)p->txt.Find(p->sel))->subzeilen.Find(p->subsel));
			}
			else
			{
				z=((zeilen*)p->txt.Find(p->sel));
			}
			if (z->check) z->pressed=!z->pressed;
			if (z->mx)
			{
				if (z->pressed)
				{
					m=0;
				}
				else
				{
					zeilen *z2=z;
					while (z2->base.prev && ((zeilen*)z2->base.prev)->mx && ((zeilen*)z2->base.prev)->nr==z->nr)
					{
						z2=(zeilen*)z2->base.prev;
					}
					while (z2->base.next && z2->mx && z2->nr==z->nr)
					{
						z2->pressed=0;
						z2=(zeilen*)z2->base.next;
					}
					z->pressed=1;
				}
			}
		}
		p->paint();
//		IgnoreNothing();
//		if (p->grabed) XUngrabPointer(display(),event->xbutton.time);
		UngrabPointer();
		Mode(GM_BUTTON1|GM_BUTTON3|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ALLMOVES|GM_MINIMALMOVE);
		if (p->sopen)
		{// MPOP: array
			p->subwin.Close();
			p->sopen=0;
		}
		p->win.CloseU();
		p->open=0;
	}
	return m;
}
int popup::GMouseMove(int mx,int my,int rx,int ry,unsigned int state,int button)
{
	int osel=p->sel,ossel=p->subsel;// MPOP: array
	int wsel=p->sel,wssel=p->subsel;// MPOP: array

	p->go_down=0;
	p->go_up=0;
	if (!p->sopen)
	{
		if (p->tohigh)
		{
			if (wsel<1) wsel=1;
			if (my-p->tohighstart>p->h)
			{
				p->go_down=1;
				p->tohighstart+=((zeilen*)p->txt.Find(wsel))->h;
				if (gg_height()-p->tohighstart<p->h)
				{
					p->tohighstart=gg_height()-p->h;
				}
				gadget::Move(0,-p->tohighstart);
			}
			if (my-p->tohighstart<0)
			{
				p->go_up=1;
				p->tohighstart-=((zeilen*)p->txt.Find(wsel))->h;
				if (p->tohighstart<0)
				{
					p->tohighstart=0;
				}
				gadget::Move(0,-p->tohighstart);
			}
		}
	}
	else if (p->subtohigh)
	{// MPOP: array
		if (wsel<1) wsel=1;
		if (wssel<1) wssel=1;
		if (my+p->y-p->sy-p->tohighstart>p->sh)
		{
			p->go_down=1;
			p->subtohighstart+=((zeilen*)((zeilen*)p->txt.Find(wsel))->subzeilen.Find(wssel))->h;
			if (p->subgg.gg_height()-p->subtohighstart<p->sh)
			{
				p->subtohighstart=p->subgg.gg_height()-p->sh;
			}
			p->subgg.Move(0,-p->subtohighstart);
		}
		if (my+p->y-p->sy-p->tohighstart<0)
		{
			p->go_up=1;
			p->subtohighstart-=((zeilen*)((zeilen*)p->txt.Find(wsel))->subzeilen.Find(wssel))->h;
			if (p->subtohighstart<0)
			{
				p->subtohighstart=0;
			}
			p->subgg.Move(0,-p->subtohighstart);
		}
	}

	p->select(mx,my);
	p->paint();
	return ((osel!=p->sel) || (ossel!=p->subsel))?GSR_MESSAGE:0;// MPOP: array
}
void popup::GSetLimits(void)
{
}

#endif
