#ifndef CODE_GROUP_CXX
#define CODE_GROUP_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: rubbergroup.cxx,v 1.2 1998/07/31 06:35:21 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 <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include "types.h"
#include <Xclasses/gadget.h>
#include <Xclasses/Ddraw.h>
#include <Xclasses/popup.h>
#include <Xclasses/tools.h>
#include <Xclasses/rubbergroup.h>
#include "include/rubbergroup_p.h"


rubbergroup::rubbergroup(void) : gadget(), Ddraw(this)
{
	while (!(p=new prubbergroup))
	{
		outOfMemory("rubbergroup");
	}
	p->o=this;
	Name("rubbergroup");
	BorderSize(2);
	p->pmenu.ParentClass(this);
	p->pmenu.Name("popup");
}
prubbergroup::prubbergroup() : pmenu()
{
	aused=0;
	asize=0;
	subs=NULL;
	horiz=0;
	ignorelimit=0;
	moved=0;
	rubgc=0;
}
rubbergroup::~rubbergroup(void)
{
	if (win) Free();
	if (p) delete p;
}
prubbergroup::~prubbergroup()
{
	if (asize>0)
	{
		if (subs) delete subs;
		asize=0;
	}
}

void rubbergroup::Horiz(void)
{
	p->horiz=1;
}
void rubbergroup::Vert(void)
{
	p->horiz=0;
}

#define BLOCK 10

void rubbergroup::Add(class gadget *gg)
{
	p->aused++;
	if (p->aused>p->asize)
	{
		struct sub *c;
		if (c=new sub[p->asize+BLOCK])
		{
			for(int i=0;i<(p->aused-1);i++)
			{
				c[i]=p->subs[i];
			}
			if (p->subs) delete p->subs;
			p->subs=c;
			p->asize=p->asize+BLOCK;
		}
		else
		{
			p->aused--;
			return;
		}
	}
	p->subs[p->aused-1].obj=gg;
	p->subs[p->aused-1].width=1;
	p->subs[p->aused-1].mvgg=NULL;
	gg->ParentClass(this);
}
int rubbergroup::Create(void)
{
	Background(col_background());
	if (gadget::Create())
	{
		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);
		for(int i=0;i<p->aused-1;i++)
		{
			p->subs[i].mvgg=new gadget;
			p->subs[i].mvgg->Name("moveknob");
			p->subs[i].mvgg->ParentClass(this);
		}
		p->SetSize(2);
		for(int i=0;i<p->aused;i++)
		{
			p->subs[i].obj->Create();

			if (p->subs[i].mvgg)
			{
				p->subs[i].mvgg->SendEventTo(this);
				p->subs[i].mvgg->Background(col_background());
				p->subs[i].mvgg->Create();
				p->subs[i].mvgg->userdata=(void*)(i+1);
				p->subs[i].mvgg->Mode(GM_ONEEXPOSE|GM_MOUSEMOVE|GM_BUTTON1|GM_BUTTON3|GM_MINIMALMOVE);
				if (p->horiz)
					p->subs[i].mvgg->MCursor(XC_sb_h_double_arrow);
				else
					p->subs[i].mvgg->MCursor(XC_sb_v_double_arrow);
			}
		}
		Mode(GM_ONEEXPOSE|GM_MOUSEMOVE|GM_BUTTON3|GM_MINIMALMOVE);
		p->pmenu.AddHeadline("Settings");
		p->pmenu.Add("set startup widths",NULL,1);
		p->pmenu.SendEventTo(this);
		p->pmenu.Create();
		return 1;
	}
	return 0;
}

void rubbergroup::Free(void)
{
	for(int i=0;i<p->aused;i++)
	{
		if (p->subs[i].mvgg)
		{
			p->subs[i].mvgg->Free();
			delete p->subs[i].mvgg;
			p->subs[i].mvgg=NULL;
		}
		p->subs[i].obj->Free();
	}
	if (p->rubgc)
	{
		XFreeGC(display(),p->rubgc);
		p->rubgc=0;
	}
	p->pmenu.Free();
	gadget::Free();
}

void rubbergroup::Resize(int nwidth,int nheight)
{
//	if (width!=nwidth || height!=nheight)
	{
		gadget::Resize(nwidth,nheight);
		p->SetSize(0);
//		GExpose(NULL);
	}
}
void rubbergroup::Lock(void)
{
	for(int i=0;i<p->aused;i++)
	{
//		p->subs[i].obj->Lock();
		if (p->subs[i].mvgg)
			p->subs[i].mvgg->Lock();
	}
	gadget::Lock();
}
void rubbergroup::Unlock(void)
{
	for(int i=0;i<p->aused;i++)
	{
//		p->subs[i].obj->Unlock();
		if (p->subs[i].mvgg)
			p->subs[i].mvgg->Unlock();
	}
	gadget::Unlock();
}
void rubbergroup::Block(int b)
{
	for(int i=0;i<p->aused;i++)
	{
		p->subs[i].obj->Block(b);
		if (p->subs[i].mvgg)
			p->subs[i].mvgg->Block(b);
	}
	gadget::Block(b);
}
void rubbergroup::Unblock(int b)
{
	for(int i=0;i<p->aused;i++)
	{
		p->subs[i].obj->Unblock(b);
		if (p->subs[i].mvgg)
			p->subs[i].mvgg->Unblock(b);
	}
	gadget::Unblock(b);
}
void rubbergroup::GSetLimits(void)
{
	if (p->horiz)
	{
		minwidth=p->aused*(RULERSIZE+1);
		minheight=0;
	}
	else
	{
		minwidth=0;
		minheight=p->aused*(RULERSIZE+1);
	}

	for(int i=0;i<p->aused;i++)
	{
		p->subs[i].obj->GSetLimits();
		if (p->horiz)
		{
			minwidth+=p->subs[i].obj->gg_minwidth();
			if (p->subs[i].obj->gg_minheight()>minheight)
				minheight=p->subs[i].obj->gg_minheight();
		}
		else
		{
			minheight+=p->subs[i].obj->gg_minheight();
			if (p->subs[i].obj->gg_minwidth()>minwidth)
				minwidth=p->subs[i].obj->gg_minwidth();
		}
	}
}

void prubbergroup::SetSize(int md)
{
	int x=0,y=0,w,h;

	if (md>0 || (md==0 && !moved))
	{
		if (horiz)
		{
			w=o->width-(aused-1)*RULERSIZE; //+RULERSIZE/2;
		}
		else
		{
			w=o->height-(aused-1)*RULERSIZE; //+RULERSIZE/2;
		}
		w=w/aused;
		for(int i=0;i<aused;i++)
		{
			subs[i].width=w; //*subs[i].obj->gg_weight()/100;
			subs[i].tomin=0;
			subs[i].tomax=0;
		}
	}
	else
	{
		w=0;
		for(int i=0;i<aused-1;i++)
		{
			w+=subs[i].width+RULERSIZE;
			subs[i].tomin=0;
			subs[i].tomax=0;
		}
		subs[aused-1].tomin=0;
		subs[aused-1].tomax=0;
		if (md>=0)
		{
			if (horiz)
				subs[aused-1].width=o->width-w-RULERSIZE/2+1;
			else
				subs[aused-1].width=o->height-w-RULERSIZE/2+1;
		}
	}

	if (md>0 || !ignorelimit)
	{
		int fertig,help=0;
		int wmin=0,wmax=0,cmin=0,cmax=0;
		int tomins=0,tomaxs=0;
		do
		{
			for(int i=0;i<aused;i++)
			{
				if (tomaxs)
				{
					if (!subs[i].tomax)
					{
						subs[i].width=subs[i].width+wmax/(aused-cmax);
					}
				}
				if (tomins)
				{
					if (!subs[i].tomin)
					{
						subs[i].width=subs[i].width-wmin/(aused-cmin);
					}
				}
				subs[i].tomin=0;
				subs[i].tomax=0;
			}
			fertig=1;
			wmin=0; wmax=0; cmin=0; cmax=0;
			for(int i=0;i<aused;i++)
			{
				if (horiz)
				{
					if (subs[i].width<subs[i].obj->gg_minwidth())
					{
						wmin+=subs[i].obj->gg_minwidth()-subs[i].width;
						subs[i].width=subs[i].obj->gg_minwidth();
						subs[i].tomin=1;
						cmin++;
						fertig=0;
					}
					else if (subs[i].width>subs[i].obj->gg_maxwidth())
					{
						wmax+=subs[i].width-subs[i].obj->gg_maxwidth();
						subs[i].width=subs[i].obj->gg_maxwidth();
						subs[i].tomax=1;
						cmax++;
						fertig=0;
					}
				}
				else
				{
					if (subs[i].width<subs[i].obj->gg_minheight())
					{
						wmin+=subs[i].obj->gg_minheight()-subs[i].width;
						subs[i].width=subs[i].obj->gg_minheight();
						subs[i].tomin=1;
						cmin++;
						fertig=0;
					}
					else if (subs[i].width>subs[i].obj->gg_maxheight())
					{
						wmax+=subs[i].width-subs[i].obj->gg_maxheight();
						subs[i].width=subs[i].obj->gg_maxheight();
						subs[i].tomax=1;
						cmax++;
						fertig=0;
					}
				}
			}
			tomins=0; tomaxs=0;
			if (cmax>cmin) tomaxs=1; else tomins=1;
			help++;
		}
		while (!fertig && help<100);
		if (help>95) warn_printf("Calculation looping, breaking\n",o);
	}
	for(int i=0;i<aused;i++)
	{
		if (horiz)
		{
			subs[i].pos=x;
			w=subs[i].width;
			h=o->height;
			if (h>subs[i].obj->gg_maxheight())
				h=subs[i].obj->gg_maxheight();
			x+=w+RULERSIZE;
		}
		else
		{
			subs[i].pos=y;
			h=subs[i].width;
			w=o->width;
			if (w>subs[i].obj->gg_maxwidth())
				w=subs[i].obj->gg_maxwidth();
			y+=h+RULERSIZE;
		}
	}
	x=0; y=0; // 2 Schleifen, da group::Resize() Expose-Events verarbeitet!
	for(int i=0;i<aused;i++)
	{
		if (horiz)
		{
			w=subs[i].width;
			h=o->height;
			if (h>subs[i].obj->gg_maxheight())
				h=subs[i].obj->gg_maxheight();
		}
		else
		{
			h=subs[i].width;
			w=o->width;
			if (w>subs[i].obj->gg_maxwidth())
				w=subs[i].obj->gg_maxwidth();
		}
		if (md>1)
		{
			subs[i].obj->Dimensions(o->gg_win(),x,y,w,h);
			if (subs[i].mvgg)
			{
				if (horiz)
					subs[i].mvgg->Dimensions(o->gg_win(),x+w,y+KNOBOFFSET,KNOBSIZE,KNOBSIZE);
				else
					subs[i].mvgg->Dimensions(o->gg_win(),x+KNOBOFFSET,y+h,KNOBSIZE,KNOBSIZE);
			}
		}
		else
		{
			subs[i].obj->Move(x,y);
			subs[i].obj->Resize(w,h);
			if (subs[i].mvgg)
			{
				if (horiz)
					subs[i].mvgg->Move(x+w,y+KNOBOFFSET);
				else
					subs[i].mvgg->Move(x+KNOBOFFSET,y+h);
			}
		}
		if (horiz)
		{
			x+=w+RULERSIZE;
		}
		else
		{
			y+=h+RULERSIZE;
		}
	}
	if (ignorelimit)
	{
		for(int i=1;i<aused;i++)
		{
			subs[i].minleft=subs[i-1].pos+RULERSIZE/2;
			subs[i].minright=subs[i].pos+subs[i].width-RULERSIZE/2;
		}
	}
	else
	{
		if (horiz)
		{
			for(int i=1;i<aused;i++)
			{
				subs[i].minleft=subs[i-1].pos+subs[i-1].obj->gg_minwidth()+RULERSIZE/2-1;
				subs[i].minright=subs[i].pos+subs[i].width-subs[i].obj->gg_minwidth()-(RULERSIZE/2+1);
			}
		}
		else
		{
			for(int i=1;i<aused;i++)
			{
				subs[i].minleft=subs[i-1].pos+subs[i-1].obj->gg_minheight()+RULERSIZE/2-1;
				subs[i].minright=subs[i].pos+subs[i].width-subs[i].obj->gg_minheight()-(RULERSIZE/2+1);
			}
		}
	}
}

void rubbergroup::GExpose(XEvent *event)
{
	if (wasfor && event)
	{
		if (!strcmp(wasfor->Name(),"moveknob"))
			borderUp(wasfor,0,0,KNOBSIZE,KNOBSIZE);
		else
			wasfor->GExpose(event);
	}
	else
	{
		for(int i=1;i<p->aused;i++)
		{
			if (p->horiz)
			{
				XSetForeground(display(),gc,col_shadow2());
				XDrawLine(display(),win,gc,p->subs[i].pos-RULERSIZE/2-1,0,p->subs[i].pos-RULERSIZE/2-1,height);
				XSetForeground(display(),gc,col_shine2());
				XDrawLine(display(),win,gc,p->subs[i].pos-RULERSIZE/2,0,p->subs[i].pos-RULERSIZE/2,height);
			}
			else
			{
				XSetForeground(display(),gc,col_shadow2());
				XDrawLine(display(),win,gc,0,p->subs[i].pos-RULERSIZE/2-1,width,p->subs[i].pos-RULERSIZE/2-1);
				XSetForeground(display(),gc,col_shine2());
				XDrawLine(display(),win,gc,0,p->subs[i].pos-RULERSIZE/2,width,p->subs[i].pos-RULERSIZE/2);
			}
		}
	}
}
void prubbergroup::draw(void)
{
	if (horiz)
	{
		XDrawLine(o->display(),o->win,rubgc,rubpos,0,rubpos,o->height);
		XDrawLine(o->display(),o->win,rubgc,rubpos+1,0,rubpos+1,o->height);
	}
	else
	{
		XDrawLine(o->display(),o->win,rubgc,0,rubpos,o->width,rubpos);
		XDrawLine(o->display(),o->win,rubgc,0,rubpos+1,o->width,rubpos+1);
	}
}
int rubbergroup::GActivate(XEvent *event,int why)
{
	if (p->pmenu.PopupOpen())
	{
		return p->pmenu.GActivate(event,why) & GAR_ACTIVE;
	}
	if (why==GA_BUTTONPRESS && event->xbutton.button==3)
	{
		int x,y;
		GetPositionOnRoot(display(),win,x,y);
		x+=event->xbutton.x;
		y+=event->xbutton.y;
		p->pmenu.Open(x,y);
		return GAR_ACTIVE;
	}
	if (wasfor)
	{
		borderDownFill(wasfor,0,0,KNOBSIZE,KNOBSIZE);
		p->rubpos=p->subs[(int)wasfor->userdata].pos-RULERSIZE/2-1;
		p->draw();
		return GAR_ACTIVE;
	}
	return 0;
}
int rubbergroup::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: // reset
					p->SetSize(1);
					XClearWindow(display(),win);
					GExpose(NULL);
					break;
			}
		}
		return 0;
	}

	if (wasfor)
	{
		gadget *oldwas=wasfor;
		p->draw();
		p->moved=1;

		p->subs[(int)wasfor->userdata].width-=p->rubpos-(p->subs[(int)wasfor->userdata].pos-RULERSIZE/2-1);
		p->subs[((int)wasfor->userdata)-1].width+=p->rubpos-(p->subs[(int)wasfor->userdata].pos-RULERSIZE/2-1);

		p->SetSize(-1);
		XClearWindow(display(),win);
		GExpose(NULL);
		borderUpFill(oldwas,0,0,KNOBSIZE,KNOBSIZE);
		return GSR_SELECTION;
	}
	return 0;
}
int rubbergroup::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)
	{
		p->draw();

		if (p->horiz)
			p->rubpos=p->subs[(int)wasfor->userdata].pos+mx-RULERSIZE/2-1-KNOBSIZE/2;
		else
			p->rubpos=p->subs[(int)wasfor->userdata].pos+my-RULERSIZE/2-1-KNOBSIZE/2;

		if (p->rubpos>p->subs[(int)wasfor->userdata].minright)
		{
			p->rubpos=p->subs[(int)wasfor->userdata].minright;
		}
		if (p->rubpos<p->subs[(int)wasfor->userdata].minleft)
		{
			p->rubpos=p->subs[(int)wasfor->userdata].minleft;
		}
		p->draw();
	}
	return 0;
}
void rubbergroup::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;
	}
}
void rubbergroup::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;
	}
}

#endif
