#ifndef CODE_SCROLLER_CXX
#define CODE_SCROLLER_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: scroller.cxx,v 1.5 1998/10/20 19:03:04 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 "types.h"
#include "../include/defaults.h"
#include <Xclasses/gadget.h>
#include <Xclasses/Xcolors.h>
#include <Xclasses/Ddraw.h>
#include <Xclasses/scroller.h>
#include "include/scroller_p.h"


#define WTICK	3

scroller::scroller(): gadget(), Ddraw(this)
{
	while (!(p=new pscroller))
	{
		outOfMemory("scroller");
	}
	p->o=this;
	Name("scroller");
	p->b1.ParentClass(this); p->b1.Name("up");
	p->b2.ParentClass(this); p->b2.Name("down");
	p->a1.ParentClass(this);
	p->a2.ParentClass(this);
}
scroller::~scroller()
{
	if (win) Free();
	if (p) delete p;
}
pscroller::pscroller() : b1(), b2(), a1(), a2()
{
	p_laenge=0;
	op_laenge=0;
	p_pos=0;
	op_pos=0;
	anzahl=0;
	position=0;
	sichtbar=1;
	ticker=0;

	horiz=0;
	buts=0;
	mmove=0;
	mtick=0;
	edgebuts=defaults.scroller_buttonstogether?0:1;
	emboss=defaults.scroller_emboss;
	step=1;
}
pscroller::~pscroller()
{
}

void scroller::Horiz(void)
{
	p->horiz=1;
	if (win) GExpose(NULL);
}
void scroller::Vert(void)
{
	p->horiz=0;
	if (win) GExpose(NULL);
}
void scroller::Arrows(void)
{
	p->buts=1;
}
void scroller::Items(int c)
{
	if (c<0) c=0;
	p->anzahl=c;
//	if (c<p->sichtbar) p->sichtbar=c;
	Position(p->position);
	if (win)
	{
		p->knopf();
		if (locked) borderlocked(this,0,0,width,height);
	}
}
void scroller::Visible(int c)
{
//	if (c>p->anzahl) c=p->anzahl;
	if (c<1) c=1;
	p->sichtbar=c;
	Position(p->position);
	if (win)
	{
		p->knopf();
		if (locked) borderlocked(this,0,0,width,height);
	}
}
void scroller::Position(int c)
{
	if (c>=p->anzahl-p->sichtbar)
	{
		c=p->anzahl-p->sichtbar;
	}
	if (c<0) c=0;
	p->position=c;
	if (win)
	{
		p->knopf();
		if (locked) borderlocked(this,0,0,width,height);
	}
}
void scroller::ArrowStep(int s)
{
	p->step=s;
}
int scroller::Position(void)
{
	return p->position;
}
int scroller::Create(void)
{ 
	p->edgebuts=defaults.scroller_buttonstogether?0:1;
	p->emboss=defaults.scroller_emboss;
	Background(col_darkbackground());
	switch (p->emboss)
	{
		case pOS:
			BorderSize(3);
			break;
		case irix:
			BorderSize(1);
			break;
		default:
			BorderSize(2);
			break;
	}
	KeyBorder();
	
    if (gadget::Create())
    {
    	char *res=GetResource("hilight");
    	if (!res || strcasecmp(res,"True"))
		{
			Mode(GM_BUTTON1|GM_BUTTON2|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ACTIVETICK|GM_KEY|GM_MINIMALMOVE);
			p->above=0;
		}
		else
		{
			Mode(GM_BUTTON1|GM_BUTTON2|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ACTIVETICK|GM_KEY|GM_MINIMALMOVE|GM_ABOVE|GM_LOCKATONCE);
			p->above=1;
		}

        if (!p->p_laenge) p->p_laenge=S_LENGTH;

		if (p->buts)
		{
			p->a1.Direction(p->horiz?ARROW_LEFT:ARROW_UP);
			p->a2.Direction(p->horiz?ARROW_RIGHT:ARROW_DOWN);
			p->b1.SendTicks();
			p->b1.NoKeyBorder();
			p->b1.Object(p->a1);

#define B1_X		(p->horiz?(p->edgebuts?BUTEMBOSS:width-BUTH*2-BUTEMBOSS):BUTEMBOSS)
#define B1_Y		(p->horiz?BUTEMBOSS:(p->edgebuts?BUTEMBOSS:height-BUTH*2-BUTEMBOSS))
#define B1_W		(p->horiz?BUTH:width-BUTEMBOSS*2)
#define B1_H		(p->horiz?height-BUTEMBOSS*2:BUTH)
			p->b1.Dimensions(win,B1_X,B1_Y,B1_W,B1_H);
			p->b2.SendTicks();
			p->b2.NoKeyBorder();
			p->b2.Object(p->a2);

#define B2_X	(p->horiz?width-BUTH-BUTEMBOSS:BUTEMBOSS)
#define B2_Y	(p->horiz?BUTEMBOSS:height-BUTH-BUTEMBOSS)
#define B2_W	(p->horiz?BUTH:width-BUTEMBOSS*2)
#define B2_H	(p->horiz?height-BUTEMBOSS*2:BUTH)
			p->b2.Dimensions(win,B2_X,B2_Y,B2_W,B2_H);
			if (p->b1.Create())
			{
				p->b1.SendEventTo(this);
				if (p->b2.Create())
				{
					p->b2.SendEventTo(this);
			        return 1;
				}
			}
		}
		return 1;
    } 
    return 0;
}
void scroller::Free(void)
{
	if (p->buts)
	{
		p->b1.Free();
		p->b2.Free();
	}
	gadget::Free();
}
void scroller::Resize(int w,int h)
{
	gadget::Resize(w,h);

	if (p->buts)
	{
		p->b1.Move(B1_X,B1_Y);
		p->b1.Resize(B1_W,B1_H);
		p->b2.Move(B2_X,B2_Y);
		p->b2.Resize(B2_W,B2_H);
	}
	p->knopf();
}
void scroller::Lock(void)
{
	p->b1.Lock();
	p->b2.Lock();
	gadget::Lock();
}
void scroller::Unlock(void)
{
	p->b1.Unlock();
	p->b2.Unlock();
	gadget::Unlock();
}
void scroller::Block(int b)
{
	p->b1.Block(b);
	p->b2.Block(b);
	gadget::Block(b);
}
void scroller::Unblock(int b)
{
	p->b1.Unblock(b);
	p->b2.Unblock(b);
	gadget::Unblock(b);
}
void pscroller::knopf(int m)
{ // 0: nur wenn noetig
  // 1: immer neu
  // 2: immer neu hilight

	if (anzahl>sichtbar)
	{
		p_laenge=SP_LENGTH*sichtbar/anzahl;
		if (p_laenge<5) p_laenge=5;
		p_pos=(SP_LENGTH-p_laenge)*position/(anzahl-sichtbar);
	}
	else
	{
		p_laenge=SP_LENGTH;
		p_pos=0;
	}
	if (edgebuts&&buts)
	{
		p_pos+=BUTH;
	}

	if (m || p_pos!=op_pos || p_laenge!=op_laenge)
	{
		op_pos=p_pos;
		op_laenge=p_laenge;

		XSetForeground(o->display(),o->gc,o->col_darkbackground());
		if (p_pos>0)
		{
			XFillRectangle(o->display(),o->win,o->gc,o->RealSize()+(horiz&&edgebuts&&buts?BUTH:0),o->RealSize()+(!horiz&&edgebuts&&buts?BUTH:0),S_X1-(horiz&&edgebuts&&buts?BUTH:0),S_Y1-(!horiz&&edgebuts&&buts?BUTH:0));
		}
		if (p_pos+p_laenge-(edgebuts&&buts?BUTH:0)<S_MAX)
		{
			XFillRectangle(o->display(),o->win,o->gc,S_X2,S_Y2,o->width-o->RealSize()-S_X2-SP_BUTSX2,o->height-o->RealSize()-S_Y2-SP_BUTSY2);
		}
		if (m==2 || (above && o->active & GA_ABOVE))
			XSetForeground(o->display(),o->gc,o->col_shinebackground());
		else
			XSetForeground(o->display(),o->gc,o->col_selected());
		XFillRectangle(o->display(),o->win,o->gc,S_X,S_Y,S_WIDTH-1-S_X,S_HEIGHT-1-S_Y);

		if (p_laenge>14)
		{
			for(int j=0;j<2;j++)
			{
				if (j==1)
					XSetForeground(o->display(),o->gc,o->col_shine2());
				else
					XSetForeground(o->display(),o->gc,o->col_shadow2());
				for(int i=-4+j;i<=3;i+=3)
				{
					if (horiz)
						XDrawLine(o->display(),o->win,o->gc,S_X+p_laenge/2+i,S_Y+3,S_X+p_laenge/2+i,S_HEIGHT-4);
					else
						XDrawLine(o->display(),o->win,o->gc,S_X+3,S_Y+p_laenge/2+i,S_WIDTH-4,S_Y+p_laenge/2+i);
				}
			}
		}

		XSetForeground(o->display(),o->gc,o->col_shine());
		XDrawLine(o->display(),o->win,o->gc,S_X,S_Y,S_WIDTH-1,S_Y);
		XDrawLine(o->display(),o->win,o->gc,S_X,S_Y,S_X,S_HEIGHT-1);
		XSetForeground(o->display(),o->gc,o->col_shine2());
		XDrawLine(o->display(),o->win,o->gc,S_X+1,S_Y+1,S_WIDTH-1-1,S_Y+1);
		XDrawLine(o->display(),o->win,o->gc,S_X+1,S_Y+1,S_X+1,S_HEIGHT-1-1);

		XSetForeground(o->display(),o->gc,o->col_shadow());
		XDrawLine(o->display(),o->win,o->gc,S_X,S_HEIGHT-1,S_WIDTH-1,S_HEIGHT-1);
		XDrawLine(o->display(),o->win,o->gc,S_WIDTH-1,S_Y,S_WIDTH-1,S_HEIGHT-1);
		XSetForeground(o->display(),o->gc,o->col_shadow2());
		XDrawLine(o->display(),o->win,o->gc,S_X+1,S_HEIGHT-1-1,S_WIDTH-1-1,S_HEIGHT-1-1);
		XDrawLine(o->display(),o->win,o->gc,S_WIDTH-1-1,S_Y+1,S_WIDTH-1-1,S_HEIGHT-1-1);	
	}
}
void scroller::GExpose(XEvent *event)
{
	if (wasfor)
	{
		wasfor->GExpose(event);
	}
	else
	{
		switch (p->emboss)
		{
			case pOS:
				borderEmboss(this,S_BUTS0X,S_BUTS0Y,width-S_BUTS1X*2,height-S_BUTS1Y*2);
				break;
			case irix:
				borderUp(this,S_BUTS0X,S_BUTS0Y,width-S_BUTS0X*2,height-S_BUTS0Y*2);
				break;
			default:
				borderDown(this,S_BUTS0X,S_BUTS0Y,width-S_BUTS0X*2,height-S_BUTS0Y*2);
				break;
		}
		p->knopf(1);
		if (locked) borderlocked(this,S_BUTS0X,S_BUTS0Y,width-S_BUTS0X*2,height-S_BUTS0Y*2);
		if (active & GA_KEY) bordermark(this,S_BUTS0X,S_BUTS0Y,width-S_BUTSX,height-S_BUTSY); else borderunmark(this,0,0,width-S_BUTSX,height-S_BUTSY);
		if (p->buts && p->emboss==pOS)
		{
			XSetForeground(display(),gc,col_background());
			if (p->edgebuts)
			{
				XFillRectangle(display(),win,gc,0,0,p->horiz?BUTH:width,p->horiz?height:BUTH);
				XFillRectangle(display(),win,gc,p->horiz?width-BUTH:0,p->horiz?0:height-BUTH,p->horiz?BUTH:width,p->horiz?height:BUTH);
			}
			else
			{
				XFillRectangle(display(),win,gc,p->horiz?width-BUTH*2:0,p->horiz?0:height-BUTH*2,p->horiz?BUTH*2:width,p->horiz?height:BUTH*2);
			}
		}
	}
}
void scroller::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor && reason!=GB_KEY)
	{
		wasfor->GActionPress(mx,my,rx,ry,state,button,reason);
	}
	else
	{
		if (reason==GB_KEY) bordermark(this,0,0,width-S_BUTSX,height-S_BUTSY);
	}
}
void scroller::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor && reason!=GB_KEY)
	{
		wasfor->GActionRelease(mx,my,rx,ry,state,button,reason);
	}
	else
	{
		if (reason==GB_KEY) borderunmark(this,0,0,width-S_BUTSX,height-S_BUTSY);
	}
}
int scroller::GMouseMove(int mx,int my,int rx,int ry,unsigned int state,int button)
{
	if (p->mmove)
	{
		int ps;

		if (p->horiz)
		{
//			ps=(mx-p->offset+(p->p_laenge>>1))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
			ps=(mx-p->offset-(p->edgebuts&&p->buts?BUTH:0))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
		}
		else
		{
//			ps=(my-p->offset+(p->p_laenge>>1))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
			ps=(my-p->offset-(p->edgebuts&&p->buts?BUTH:0))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
		}
		Position(ps);
		return GSR_SELECTION|GSR_MESSAGE;
	}
	return 0;
}
int scroller::GActivate(XEvent *event,int why)
{
	if (wasfor && why!=GA_KEY)
	{
		if (why==GA_TICK)
		{
			if (wasfor->GActivate(event,why))
			{
//				if (p->ticker>0)
//				{
//					p->ticker--;
//				}
//				else
				{
					if (wasfor==&p->b1)
					{ // up
						Position(p->position-p->step);
					}
					else
					{ //down
						Position(p->position+p->step);
					}
					return GAR_ACTIVE;
				}
			}
			return 0;
		}
		else
		{
			if (why==GA_ABOVE)
			{
				active&=~GA_ABOVE; // hack?
				p->knopf(1);
			}
			wasfor->GActivate(event,why);
			return GAR_ACTIVE;
		}
	}
	else
	{
		if (why==GA_KEY)
		{
			int c;
			char taste[5];
			KeySym key;

			c=XLookupString((XKeyEvent*)event,taste,sizeof(taste),&key,0);
			switch (key)
			{
				case XK_Up:
				case XK_Left:
					Position(p->position-p->step);
					return GAR_ACTIVE;
					break;
				case XK_Down:
				case XK_Right:
					Position(p->position+p->step);
					return GAR_ACTIVE;
					break;
				case XK_Home:
					Position(0);
					return GAR_ACTIVE;
					break;
				case XK_End:
					Position(p->anzahl);
					return GAR_ACTIVE;
					break;
				default:
					c=DefaultKeyHandler(event);
					break;
			}
			return 0;
		}
		else if (why==GA_ABOVE)
		{
			p->knopf(2);
			return GAR_ACTIVE;
		}
		if (p->anzahl>p->sichtbar)
		{
			if (why==GA_BUTTONPRESS)
			{
				ActivateKey();
				if (event->xbutton.button==2)
				{
					int ps;

					if (p->horiz)
					{
						ps=(event->xbutton.x-(p->edgebuts&&p->buts?BUTH:0)-(p->p_laenge>>1))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
					}
					else
					{
						ps=(event->xbutton.y-(p->edgebuts&&p->buts?BUTH:0)-(p->p_laenge>>1))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
					}
					Position(ps);
					p->ticker=WTICK;
					p->mmove=1;
					p->offset=p->p_laenge>>1;
				}
				if (event->xbutton.button==1)
				{
					int ps,d=0;

					if (p->horiz)
					{
						if (event->xbutton.x-(p->edgebuts&&p->buts?BUTH:0)>=(S_LENGTH-p->p_laenge)*p->position/(p->anzahl-p->sichtbar) &&
					    	event->xbutton.x-(p->edgebuts&&p->buts?BUTH:0)<=(S_LENGTH-p->p_laenge)*p->position/(p->anzahl-p->sichtbar)+p->p_laenge)
						{
							ps=p->position;
							d=event->xbutton.x-p->p_pos;;
						}
						else
						{
							ps=(event->xbutton.x-(p->edgebuts&&p->buts?BUTH:0)-(p->p_laenge>>1))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
						}
					}
					else
					{
						if (event->xbutton.y-(p->edgebuts&&p->buts?BUTH:0)>=(S_LENGTH-p->p_laenge)*p->position/(p->anzahl-p->sichtbar) &&
					    	event->xbutton.y-(p->edgebuts&&p->buts?BUTH:0)<=(S_LENGTH-p->p_laenge)*p->position/(p->anzahl-p->sichtbar)+p->p_laenge)
						{
							ps=p->position;
							d=event->xbutton.y-p->p_pos;;
						}
						else
						{
							ps=(event->xbutton.y-(p->edgebuts&&p->buts?BUTH:0)-(p->p_laenge>>1))*(p->anzahl-p->sichtbar)/(S_LENGTH-p->p_laenge);
						}
					}
					p->ticker=WTICK;
					if (ps==p->position)
					{
						p->mmove=1;
						p->offset=d;
					}
					else if (ps<p->position)
					{
						Position(p->position-p->sichtbar);
						p->mtick=1;
					}
					else
					{
						Position(p->position+p->sichtbar);
						p->mtick=2;
					}
				}
				return GAR_ACTIVE;
			}
			else if (why==GA_TICK)
			{
				if (p->ticker>0)
				{
					if (p->ticker--==3) return GAR_ACTIVE; else return 0;
				}
				else
				{
					if (p->mtick==1)
					{
						Position(p->position-p->sichtbar);
						return GAR_ACTIVE;
					}
					if (p->mtick==2)
					{
						Position(p->position+p->sichtbar);
						return GAR_ACTIVE;
					}
				}
			}
		}
	}
	return 0;
}
int scroller::GSelected(XEvent *event,int why,int wasabove)
{
	if (wasfor && why!=GA_KEY)
	{
		if (why==GA_TICK)
		{
			return GSR_SELECTION|GSR_MESSAGE;
		}
		else
		{
			int r=wasfor->GSelected(event,why,wasabove);
			if (r)
			{
				if (wasfor==&p->b1)
				{ // up
					Position(p->position-p->step);
				}
				else
				{ //down
					Position(p->position+p->step);
				}
			}
			return GSR_SELECTION|GSR_MESSAGE;
		}
	}
	else
	{
		if (why==GA_BUTTONPRESS)
		{
			p->mmove=0;
			p->mtick=0;
		}
		else if (why==GA_ABOVE)
		{
			p->knopf(1);
			return 0;
		}
		return GSR_SELECTION|GSR_MESSAGE;
	}
}
void scroller::GSetLimits(void)
{
	if (p->horiz)
	{
		minwidth=25+S_BUTS+BUTEMBOSS*2+RealSize()*2;
		minheight=12;
	}
	else
	{
		minwidth=12;
		minheight=25+S_BUTS+BUTEMBOSS*2+RealSize()*2;
	}
}

#endif
