#ifndef CODE_SLIDER_CXX
#define CODE_SLIDER_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: slider.cxx,v 1.4 1999/11/20 17:54:14 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 <stdlib.h>
#include "types.h"
#include "../include/defaults.h"
#include "../include/callback.h"
#include <Xclasses/gadget.h>
#include <Xclasses/Xcolors.h>
#include <Xclasses/Ddraw.h>
#include <Xclasses/slider.h>
#include <Xclasses/callable.h>
#include "include/slider_p.h"

#define WTICK	3

slider::slider(): gadget(), Ddraw(this)
{
	while (!(p=new pslider))
	{
		outOfMemory("slider");
	}
	p->o=this;
	Name("slider");
}
slider::~slider()
{
	if (win) Free();
	if (p) delete p;
}
pslider::pslider() : tcb()
{
	p_pos=0;
	op_pos=0;
	minval=0;
	maxval=10;
	position=0;
	o_position=0;
	ticker=0;
	fonth=0;

	horiz=0;
	notext=0;
	mmove=0;
	mtick=0;
	emboss=defaults.slider_emboss;
}
pslider::~pslider()
{
}

void slider::Horiz(void)
{
	p->horiz=1;
	if (win) GExpose(NULL);
}
void slider::Vert(void)
{
	p->horiz=0;
	if (win) GExpose(NULL);
}
void slider::NoText(void)
{
	p->notext=1;
}
void slider::MinValue(int c)
{
//	if (c>=p->maxval) c=p->maxval-1;
	p->minval=c;
	Value(p->position);
	if (win)
	{
		p->knopf();
		if (locked) borderlocked(this,0,0,width,height);
	}
}
void slider::MaxValue(int c)
{
//	if (c<=p->minval) c=p->minval+1;
	p->maxval=c;
	Value(p->position);
	if (win)
	{
		p->knopf();
		if (locked) borderlocked(this,0,0,width,height);
	}
}
void slider::Value(int c)
{
	if (c<p->minval) c=p->minval;
	if (c>p->maxval) c=p->maxval;
	p->position=c;
	if (win)
	{
		p->knopf(1);
		if (locked) borderlocked(this,0,0,width,height);
	}
}
int slider::Value(void)
{
	return p->position;
}
void slider::TextCallback(void cfunc(int val,char *buf,int maxsize))
{
	p->tcb.set((CBCAST) cfunc);
	p->notext=0;
}
void slider::TextCallback(callable *cl,void (callable::*cfunc)(int val,char *buf,int maxsize))
{
	p->tcb.set(cl,(CALLFUNCTION) cfunc);
	p->notext=0;
}
int slider::Create(void)
{ 
	p->emboss=defaults.slider_emboss;
	Background(col_darkbackground());
	switch (p->emboss)
	{
		case pOS:
			BorderSize(3);
			break;
		case irix:
			BorderSize(1);
			break;
		default:
			BorderSize(2);
			break;
	}
	KeyBorder();
	if (!setlimit) GSetLimits();
	
    if (gadget::Create())
    {
    	char *res=GetResource("hilight");
    	if (!res || strcasecmp(res,"True"))
    	{
    		p->above=0;
			Mode(GM_BUTTON1|GM_BUTTON2|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ACTIVETICK|GM_KEY|GM_MINIMALMOVE);
		}
		else
		{
			p->above=1;
			Mode(GM_BUTTON1|GM_BUTTON2|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_ACTIVETICK|GM_KEY|GM_MINIMALMOVE|GM_LOCKATONCE|GM_ABOVE);
		}
        return 1;
    } 
    return 0;
}
void pslider::knopf(int m)
{ // 0: wenn noetig neu
  // 1: immer neu
  // 2: immer neu hilight

	p_pos=(SP_LENGTH-PKSIZE)*(position-minval)/(maxval-minval);

	if (m || p_pos!=op_pos || o_position!=position)
	{
		XSetForeground(o->display(),o->gc,o->col_darkbackground());
//		XFillRectangle(o->display(),o->win,o->gc,
//		               S_X1(op_pos),S_Y1(op_pos),S_WIDTH(op_pos),S_HEIGHT(op_pos));

		XFillRectangle(o->display(),o->win,o->gc,o->RealSize(),o->RealSize(),o->width-o->RealSize()-o->RealSize(),o->height-o->RealSize()-o->RealSize());

		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_X1(p_pos),S_Y1(p_pos),S_WIDTH(p_pos),S_HEIGHT(p_pos));

		XSetForeground(o->display(),o->gc,o->col_shine());
		XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos),S_Y1(p_pos),S_X2(p_pos),S_Y1(p_pos));
		XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos),S_Y1(p_pos),S_X1(p_pos),S_Y2(p_pos));
		XSetForeground(o->display(),o->gc,o->col_shine2());
		XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+1,S_Y1(p_pos)+1,S_X2(p_pos)-1,S_Y1(p_pos)+1);
		XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+1,S_Y1(p_pos)+1,S_X1(p_pos)+1,S_Y2(p_pos)-1);

		XSetForeground(o->display(),o->gc,o->col_shadow());
		XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+1,S_Y2(p_pos),S_X2(p_pos),S_Y2(p_pos));
		XDrawLine(o->display(),o->win,o->gc,S_X2(p_pos),S_Y1(p_pos)+1,S_X2(p_pos),S_Y2(p_pos));
		XSetForeground(o->display(),o->gc,o->col_shadow2());
		XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+1+1,S_Y2(p_pos)-1,S_X2(p_pos)-1,S_Y2(p_pos)-1);
		XDrawLine(o->display(),o->win,o->gc,S_X2(p_pos)-1,S_Y1(p_pos)+1+1,S_X2(p_pos)-1,S_Y2(p_pos)-1);

		XSetForeground(o->display(),o->gc,o->col_shadow2());
		if (horiz)
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+(S_WIDTH(p_pos)>>1)-1,S_Y1(p_pos)+2,S_X1(p_pos)+(S_WIDTH(p_pos)>>1)-1,S_Y2(p_pos)-2);
		else
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+2,S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1)-2,S_X2(p_pos)-2,S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1)-2);
		XSetForeground(o->display(),o->gc,o->col_shadow());
		if (horiz)
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+(S_WIDTH(p_pos)>>1)-0,S_Y1(p_pos)+1,S_X1(p_pos)+(S_WIDTH(p_pos)>>1)-0,S_Y2(p_pos)-1);
		else
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+1,S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1)-1,S_X2(p_pos)-1,S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1)-1);
		XSetForeground(o->display(),o->gc,o->col_shine());
		if (horiz)
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+(S_WIDTH(p_pos)>>1)+1,S_Y1(p_pos),S_X1(p_pos)+(S_WIDTH(p_pos)>>1)+1,S_Y2(p_pos)-1);
		else
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos),S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1),S_X2(p_pos)-0,S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1));
		XSetForeground(o->display(),o->gc,o->col_shine2());
		if (horiz)
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+(S_WIDTH(p_pos)>>1)+2,S_Y1(p_pos),S_X1(p_pos)+(S_WIDTH(p_pos)>>1)+2,S_Y2(p_pos)-2);
		else
			XDrawLine(o->display(),o->win,o->gc,S_X1(p_pos)+1,S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1)+1,S_X2(p_pos)-1,S_Y1(p_pos)+(S_HEIGHT(p_pos)>>1)+1);

		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());
		if (horiz)
			XFillRectangle(o->display(),o->win,o->gc,S_X1(p_pos)+2,S_Y1(p_pos)+((S_HEIGHT(p_pos)-fonth)>>1),S_WIDTH(p_pos)-2-2,fonth);
		else
			XFillRectangle(o->display(),o->win,o->gc,S_X1(p_pos)+5,S_Y1(p_pos)+2,S_WIDTH(p_pos)-8,S_HEIGHT(p_pos)-2-2);

		if (tcb.iscallback())
		{
			tcb.call((void*)position,(void*)txt,(void*)sizeof(txt));
		}
		else
		{
			sprintf(txt,"%d",position);
		}
		if (!notext)
			o->bordertext(o,S_X1(p_pos),S_Y1(p_pos),S_WIDTH(p_pos),S_HEIGHT(p_pos),txt);
		
		op_pos=p_pos;
		o_position=position;
	}
}
void slider::GExpose(XEvent *event)
{
	p->knopf(1);
	switch (p->emboss)
	{
		case pOS:
			borderEmboss(this,0,0,width,height);
			break;
		case irix:
			borderUp(this,0,0,width,height);
			break;
		default:
			borderDown(this,0,0,width,height);
			break;
	}
	if (locked) borderlocked(this,0,0,width,height);
	if (active & GA_KEY) bordermark(this,0,0,width,height); else borderunmark(this,0,0,width,height);
}
void slider::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (reason==GB_KEY) bordermark(this,0,0,width,height);
}
void slider::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (reason==GB_KEY) borderunmark(this,0,0,width,height);
}
int slider::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->maxval-p->minval)/(S_LENGTH-KSIZE)+p->minval;
		}
		else
		{
			ps=(my-p->offset)*(p->maxval-p->minval)/(S_LENGTH-KSIZE)+p->minval;
		}
		Value(ps);
		return GSR_SELECTION|GSR_MESSAGE;
	}
	return 0;
}
int slider::GActivate(XEvent *event,int why)
{
	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:
				Value(p->position-1);
				return GAR_ACTIVE;
				break;
			case XK_Down:
			case XK_Right:
				Value(p->position+1);
				return GAR_ACTIVE;
				break;
			case XK_Home:
				Value(p->minval);
				return GAR_ACTIVE;
				break;
			case XK_End:
				Value(p->maxval);
				return GAR_ACTIVE;
				break;
			default:
				c=DefaultKeyHandler(event);
				break;
		}
		return 0;
	}
	else if (why==GA_BUTTONPRESS)
	{
		ActivateKey();
		if (event->xbutton.button==2)
		{
			int ps;

			p->offset=KSIZE>>1;
			if (p->horiz)
			{
				ps=(event->xbutton.x-p->offset)*(p->maxval-p->minval)/(S_LENGTH-KSIZE)+p->minval;
			}
			else
			{
				ps=(event->xbutton.y-p->offset)*(p->maxval-p->minval)/(S_LENGTH-KSIZE)+p->minval;
			}
			Value(ps);
			p->ticker=WTICK;
			p->mmove=1;
		}
		if (event->xbutton.button==1)
		{
			int ps,d=0;

			if (p->horiz)
			{
				if (event->xbutton.x>=(S_LENGTH-KSIZE)*(p->position-p->minval)/(p->maxval-p->minval) &&
				    event->xbutton.x<=(S_LENGTH-KSIZE)*(p->position-p->minval)/(p->maxval-p->minval)+KSIZE)
				{
					ps=p->position;
					d=event->xbutton.x-p->p_pos;;
				}
				else
				{
					ps=(event->xbutton.x-(KSIZE>>1))*(p->maxval-p->minval)/(S_LENGTH-KSIZE)+p->minval;
				}
			}
			else
			{
				if (event->xbutton.y>=(S_LENGTH-KSIZE)*(p->position-p->minval)/(p->maxval-p->minval) &&
				    event->xbutton.y<=(S_LENGTH-KSIZE)*(p->position-p->minval)/(p->maxval-p->minval)+KSIZE)
				{
					ps=p->position;
					d=event->xbutton.y-p->p_pos;;
				}
				else
				{
					ps=(event->xbutton.y-(KSIZE>>1))*(p->maxval-p->minval)/(S_LENGTH-KSIZE)+p->minval;
				}
			}
			p->ticker=WTICK;
			if (ps==p->position)
			{
				p->mmove=1;
				p->offset=d;
			}
			else if (ps<p->position)
			{
				Value(p->position-1);
				p->mtick=1;
			}
			else
			{
				Value(p->position+1);
				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)
			{
				Value(p->position-1);
				return GAR_ACTIVE;
			}
			if (p->mtick==2)
			{
				Value(p->position+1);
				return GAR_ACTIVE;
			}
		}
	}
	else if (why==GA_ABOVE)
	{
		p->knopf(2);
		return GAR_ACTIVE;
	}
	return 0;
}
int slider::GSelected(XEvent *event,int why,int wasabove)
{
	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 slider::GSetLimits(void)
{
	int w,h,s,c=1;
	if (abs(p->maxval)>abs(p->minval)) s=abs(p->maxval); else s=abs(p->minval);

	while (s>=10)
	{
		s=s/10;
		c++;
	}
	if (!p->notext)
	{
		w=textlength(this,"00")*c;
		h=textheight(this,"100");
	}
	else
	{
		w=textheight(this,"100");
		h=textheight(this,"100");
	}
	setlimit=1;
	p->fonth=h;

	if (p->horiz)
	{
		p->ksize=w+10;
		minwidth=p->ksize+25;
		minheight=h+10+6;
		maxheight=minheight;
	}
	else
	{
		p->ksize=h+10;	
		minwidth=w+6+6;
		minheight=p->ksize+25;
		maxwidth=minwidth;
	}
}

#endif
