#ifndef CODE_GPOPUP_CXX
#define CODE_GPOPUP_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: gpopup.cxx,v 1.4 1998/07/31 06:35:18 beckerr 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/gpopup.h>
#include "../include/defaults.h"
#include "include/gpopup_p.h"
#include <Xclasses/tools.h>

gpopup::gpopup() : gadget(), Ddraw(this)
{
	while (!(p=new pgpopup))
	{
		outOfMemory("gpopup");
	}
	p->o=this;
	Name("gpopup");
	p->swin.ParentClass(this);
	p->swin.Name("gpopupwindow");
#ifdef SCHATTEN
	p->swin2.ParentClass(this);
	p->swin2.Name("gpopupshadow");
#endif
	BorderSize(BORDER);
	NoKeyBorder();
	p->closebutton.Name("gpopupclosebutton");
	p->closebutton.ParentClass(this);
	p->closebutton.specialflags|=SPF_NOKEYBORDER;
	p->pmenu.Name("gpopup_popup");
	p->pmenu.ParentClass(this);
}
pgpopup::pgpopup() : callable(), swin(),
#ifdef SCHATTEN
 swin2(),
#endif
 closebutton(), pmenu()
{
	open=0;
	sub=NULL;
	closeg=NULL;
	title=NULL;
	rubgc=0;
}
pgpopup::~pgpopup()
{
	if (title) ::free(title);
}
gpopup::~gpopup()
{
	if (win) Free();
	if (p) delete p;
}

void gpopup::Free(void)
{
	Close();
	p->sub->Free();
	p->closebutton.Free();
	if (p->rubgc)
	{
		XFreeGC(display(),p->rubgc);
		p->rubgc=0;
	}
	gadget::Free();
#ifdef SCHATTEN
	p->swin2.Destroy();
#endif
	p->swin.Destroy();
}

int gpopup::Create(void)
{
	if (win) return 1;

	if (!setlimit) GSetLimits();

	p->swin.Create(0,0,minwidth,minheight);
	p->swin.OverrideRedirect();
	p->swin.SaveUnder();
	p->swin.BorderWidth(1);
	p->swin.Background(col_background());
	p->swin.Event(KeyPressMask|FocusChangeMask);

#ifdef SCHATTEN
	p->swin2.Create(p->swin);
#endif

	Dimensions(p->swin.window(),0,0,minwidth,minheight);
    if (gadget::Create())
    {
		Mode(GM_BUTTON1|GM_BUTTON3|GM_MOUSEMOVE|GM_MINIMALMOVE|GM_ONEEXPOSE|GM_ABOVE);

		p->sub->Dimensions(win,RealSize(),RealSize()+TOPBORDER,minwidth-RealSize()*2,minheight-RealSize()*2-TOPBORDER);
		p->sub->Create();

		p->closebutton.Text("-");
		p->closebutton.Dimensions(win,0,0,TOPBORDER,TOPBORDER);
		p->closebutton.Create();
		p->closebutton.SendEventTo(this);
		p->closebutton.MCursor(XC_arrow);
		p->closebutton.ApplyXKey(XK_Escape);

		XGCValues val;
		int valmask=GCFunction|GCPlaneMask|GCForeground|GCBackground|GCSubwindowMode;
		val.plane_mask=black^white;
		val.foreground=0xffffffff;
		val.function=GXxor;
		val.subwindow_mode=IncludeInferiors;
		p->rubgc=XCreateGC(display(),win,valmask,&val);

		keyclass *kc=p->swin.Keyclass();
		kc->SendSelectTo(XK_Escape,0,p->closebutton);

		p->pmenu.AddHeadline("Popup mode");
		p->pmenu.AddSel("unblocking mode",NULL,1);
		p->pmenu.SendEventTo(this);
		p->pmenu.Create();

        return 1;
    } 
    return 0;
}
void gpopup::Open(int w)
{
	Open(w,-1,-1,-1,-1);
}
void gpopup::Open(int ox,int oy,int w)
{
#ifdef SCHATTEN
	p->swin2.Move(ox,oy);
#else
	p->swin.Move(ox,oy);
#endif
	Open(w);
}
void gpopup::Open(int w,int ax,int ay,int aw,int ah)
{
	if (GPopupOpen())
	{
#ifdef SCHATTEN
		p->swin2.Raise();
#else
		p->swin.Raise();
#endif
		return;
	}

	p->open=1;
#ifdef SCHATTEN
	p->swin2.Resize(minwidth+minwidth*w/100,minheight+minheight*w/100);
#else
	p->swin.Resize(minwidth+minwidth*w/100,minheight+minheight*w/100);
#endif
	Resize(minwidth+minwidth*w/100,minheight+minheight*w/100);
	p->swin.Update();

	int dw,dh,x,y,ww,wh;
	DisplaySize(display(),dw,dh);
	p->swin.Position(x,y);
	p->swin.Size(ww,wh);
	if (x+ww>dw) x=dw-ww;
	if (y+wh>dh) y=dh-wh;
#ifdef SCHATTEN
	p->swin2.Move(x,y);
#else
	p->swin.Move(x,y);
#endif

	p->anx=ax;
	p->any=ay;
	p->anw=aw;
	p->anh=ah;
	if (p->anw>0)
	{
		p->anim(ax,ay,aw,ah,x,y,ww,wh);
	}

#ifdef SCHATTEN
	p->swin2.Open();
#else
	p->swin.Open();
#endif
	p->IgnoreExcept();
#ifdef SCHATTEN
	p->swin2.Raise();
#else
	p->swin.Raise();
#endif
	XSetInputFocus(display(),p->swin.window(),RevertToPointerRoot,CurrentTime);
	if (p->closeg)
	{
		p->closeg->ActivateKey();
	}
}
void gpopup::Open(int ox,int oy,int w,int ax,int ay,int aw,int ah)
{
#ifdef SCHATTEN
	p->swin2.Move(ox,oy);
#else
	p->swin.Move(ox,oy);
#endif
	Open(w,ax,ay,aw,ah);
}
#define ANIMBOX	10
void pgpopup::anim(int x1,int y1,int w1,int h1,int x2,int y2,int w2,int h2)
{
	int nx=x1,ny=y1,nw=w1,nh=h1;
	for(int i=1;i<=ANIMBOX;i++)
	{
		XDrawRectangle(o->display(),o->rootwindow(),rubgc,nx,ny,nw,nh);
		XSync(o->display(),0);
//		go_sleep(50);
		nx=x1+(x2-x1)*i/ANIMBOX;
		ny=y1+(y2-y1)*i/ANIMBOX;
		nw=w1+(w2-w1)*i/ANIMBOX;
		nh=h1+(h2-h1)*i/ANIMBOX;
		XSync(o->display(),0);
	}
	nx=x1; ny=y1; nw=w1; nh=h1;
	for(int i=1;i<=ANIMBOX;i++)
	{
		XDrawRectangle(o->display(),o->rootwindow(),rubgc,nx,ny,nw,nh);
		XSync(o->display(),0);
//		go_sleep(50);
		nx=x1+(x2-x1)*i/ANIMBOX;
		ny=y1+(y2-y1)*i/ANIMBOX;
		nw=w1+(w2-w1)*i/ANIMBOX;
		nh=h1+(h2-h1)*i/ANIMBOX;
		XSync(o->display(),0);
	}
}

void gpopup::Close(void)
{
	if (!GPopupOpen()) return;
	p->IgnoreNothing();
#ifdef SCHATTEN
	p->swin2.CloseU();
#else
	p->swin.CloseU();
#endif
	p->open=0;
	if (p->anw>0)
	{
		int x,y,w,h;
		p->swin.Position(x,y);
		p->swin.Size(w,h);
		p->anim(x,y,w,h,p->anx,p->any,p->anw,p->anh);
	}
}

void pgpopup::theCloser(gadget *g,int i)
{
	o->Close();
	o->CallCallback();
}
void pgpopup::IgnoreExcept(void)
{
	if (pmenu.State(1)==RELEASED)
		 	o->IgnoreExcept(swin.window());
}
void pgpopup::IgnoreNothing(void)
{
	if (pmenu.State(1)==RELEASED)
			o->IgnoreNothing();
}
int gpopup::GPopupOpen(void)
{
	return p->open;
}

void gpopup::Apply(gadget *g)
{
	p->sub=g;
	g->ParentClass(p->swin);
}
void gpopup::CloseGadget(gadget *g)
{
	p->closeg=g;
	g->Callback(p,(CALLBACK)&pgpopup::theCloser);
}
void gpopup::Title(char *t)
{
	if (p->title) free(p->title);
	p->title=NULL;
	if (t) p->title=strdup(t);
}

void gpopup::Resize(int w,int h)
{
	gadget::Resize(w,h);
	if (p->sub)
	{
		p->sub->Resize(w-RealSize()*2,h-RealSize()*2-TOPBORDER);
	}
}
void gpopup::Lock(void)
{
	gadget::Lock();
	if (p->sub)
	{
		p->sub->Lock();
	}
}
void gpopup::Unlock(void)
{
	if (p->sub)
	{
		p->sub->Unlock();
	}
	gadget::Unlock();
}
void gpopup::Block(int b)
{
	if (p->sub)
	{
		p->sub->Block(b);
	}
	gadget::Block(b);
}
void gpopup::Unblock(int b)
{
	gadget::Unblock(b);
	if (p->sub)
	{
		p->sub->Unblock(b);
	}
}

void gpopup::GExpose(XEvent *event)
{
	if (wasfor && wasfor!=this)
	{
		wasfor->GExpose(event);
		return;
	}
	borderUpFill(this,TOPBORDER,0,width-TOPBORDER,TOPBORDER);
	borderUp(this,0,TOPBORDER,width,height-TOPBORDER);
	bordertext(this,TOPBORDER,0,width-TOPBORDER,TOPBORDER,p->title);
	p->swin.Raise();
}
void gpopup::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (p->pmenu.PopupOpen())
	{
		p->pmenu.GActionPress(mx,my,rx,ry,state,button,reason);
		return;
	}
	if (wasfor && wasfor!=this) wasfor->GActionPress(mx,my,rx,ry,state,button,reason);
}
void gpopup::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (p->pmenu.PopupOpen())
	{
		p->pmenu.GActionRelease(mx,my,rx,ry,state,button,reason);
		return;
	}
	if (wasfor && wasfor!=this) wasfor->GActionRelease(mx,my,rx,ry,state,button,reason);
}
void pgpopup::draw(int x,int y)
{
	int w,h;
	swin.Size(w,h);

	for(int i=0;i<2;i++)
	{
		XDrawRectangle(o->display(),o->rootwindow(),rubgc,x+i,y+i,w-i*2,h-i*2);
	}
	XDrawLine(o->display(),o->rootwindow(),rubgc,x,y,x+w,y+h);
	XDrawLine(o->display(),o->rootwindow(),rubgc,x,y+h,x+w,y);
}
int gpopup::GActivate(XEvent *event,int why)
{
	if (p->pmenu.PopupOpen())
	{
		return p->pmenu.GActivate(event,why) & GAR_ACTIVE;
	}

	if (wasfor && wasfor!=this) return wasfor->GActivate(event,why);

	if (why==GA_BUTTONPRESS)
	{
		if (event->xbutton.button==3)
		{
			p->pmenu.Open(event->xbutton.x_root,event->xbutton.y_root);
		}
		else
		{
			p->swin.Position(p->wx,p->wy);
			p->wx-=event->xbutton.x+2;
			p->wy-=event->xbutton.y+2;
			p->mx=event->xbutton.x;
			p->my=event->xbutton.y;
			XGrabServer(display());
			p->draw(p->wx+p->mx,p->wy+p->my);
		}
		return GAR_ACTIVE;
	}
	else if (why=GA_ABOVE)
	{
		XSetInputFocus(display(),win,RevertToPointerRoot,CurrentTime);
		return GAR_ACTIVE;
	}
	return 0;
}
int gpopup::GSelected(XEvent *event,int why,int wasabove)
{
	if (p->pmenu.PopupOpen())
	{
		if (p->pmenu.GSelected(event,why,wasabove) & GSR_SELECTION)
		{
			switch (p->pmenu.PID())
			{
				case 1: // (un)blocking mode
					if (p->pmenu.State(1)==PRESSED)
						IgnoreNothing();
					else
					 	IgnoreExcept(p->swin.window());
					break;
			}
		}
		return 0;
	}

	int r=0;
	if (wasfor && wasfor!=this) r=wasfor->GSelected(event,why,wasabove);

	if (wasfor==&p->closebutton)
	{
		if (r&GSR_SELECTION)
		{
			Close();
		}
		return 0;
	}
	if (why==GA_ABOVE)
	{
// 		XFocusChangeEvent ev;
// 		ev.type=FocusOut;
// 		ev.display=display();
// 		ev.window=p->swin.window();
// 		ev.mode=0;
// 		ev.detail=0;
// 		XSendEvent(display(),p->swin.window(),False,0,(XEvent*)&ev);
	}
	else if (why==GA_BUTTONPRESS)
	{
		p->draw(p->wx+p->mx,p->wy+p->my);

		if (p->anw>0)
		{
			int x,y,w,h;
			p->swin.Position(x,y);
			p->swin.Size(w,h);
			p->anim(x,y,w,h,p->wx+p->mx,p->wy+p->my,w,h);
		}
		XUngrabServer(display());
#ifdef SCHATTEN
		p->swin2.Move(p->wx+p->mx,p->wy+p->my);
		p->swin2.Raise();
#else
		p->swin.Move(p->wx+p->mx,p->wy+p->my);
		p->swin.Raise();
#endif
	}
	return 0;
}
int gpopup::GMouseMove(int mx,int my,int rx,int ry,unsigned int state,int button)
{
	if (p->pmenu.PopupOpen())
	{
		return p->pmenu.GMouseMove(mx,my,rx,ry,state,button);
	}

	if (wasfor && wasfor!=this) return wasfor->GMouseMove(mx,my,rx,ry,state,button);

	p->draw(p->wx+p->mx,p->wy+p->my);
	p->mx=mx;
	p->my=my;
	p->draw(p->wx+p->mx,p->wy+p->my);

	return 0;
}
void gpopup::GSetLimits(void)
{
	minwidth=RealSize()*2;
	minheight=RealSize()*2+TOPBORDER;
	if (p->sub)
	{
		p->sub->GSetLimits();
		minwidth+=p->sub->gg_minwidth();
		minheight+=p->sub->gg_minheight();
		if (p->sub->gg_maxwidth()!=NO_LIMIT) maxwidth=p->sub->gg_maxwidth()+RealSize()*2;
		if (p->sub->gg_maxheight()!=NO_LIMIT) maxheight=p->sub->gg_maxheight()+RealSize()*2+TOPBORDER;
	}
	setlimit=1;
}

#endif
