/*
	ZSlider class	V0.1alpha
*/

#include <InterfaceKit.h>
#include "ZSlider.h"

class ZSliderWindow : public BWindow 
{
	public:	ZSliderWindow(BRect frame, ZSlider *slider);
};

class ZSliderView : public BView 
{
	public:	ZSliderView(BRect frame, ZSlider *slider);
			void Draw(BRect update);
			void AttachedToWindow(void);
	private:
			ZSlider	*Slider;
			BRect	Bound;
};

ZSlider::
ZSlider
(
	BRect frame,
	const char *name,
	const char *label,
	const char *maxstr,
	void  (*SliderHook)(char *dest, int32 allowedsize, int32 value),
	BMessage *message,
	bool super,
	uint32 resizeMask, 
	uint32 flags
):
BControl(frame,name,label,message,resizeMask,flags)
{
	SetMessage(message);
	SetFlags(Flags()|B_NAVIGABLE);
	Min=0;
	Max=100;
	Act=66;
	StrSiz=255;
	MaxStr=maxstr;
	sliderhook=SliderHook;
	clickmsg=message;
	emode=super;
	notick=FALSE;
	crazy=FALSE;
}

void ZSlider::SetValue(int32 num)
{
	if(!notick)
	{
		Act=max_c(min_c(num,Max),Min);
		if(Window()->Lock())
		{
			UpdateValue();
			if(clickmsg)Window()->PostMessage(clickmsg);
			Window()->Unlock();
		}
	}
}

void ZSlider::SetValueQuiet(int32 num)
{
	if(!notick)
	{
		Act=max_c(min_c(num,Max),Min);
		if(Window()->Lock())
		{
			UpdateValue();
			Window()->Unlock();
		}
	}
}

void	ZSlider::SetMinValue(int32 val)
{
	Min=val;

	divisor=1;
	while((abs(Max-Min)/divisor)>512)
	{
		divisor*=2;
	}
}

void	ZSlider::SetMaxValue(int32 val)
{
	Max=val;
	divisor=1;
	while((abs(Max-Min)/divisor)>512)
	{
		divisor*=2;
	}
}

long	ZSlider::GetValue(){return(Act);}
long	ZSlider::GetMinValue(){return(Min);}
long	ZSlider::GetMaxValue(){return(Max);}
BRect	ZSlider::GetButton(){return(temp);}
float	ZSlider::GetMaxStrLngth(){return(MaxStrLngth);}

void	ZSlider::EDraw(BView *view, BRect frame, float inset)
{
	float w=foo.Width();
	float s=(Act*w)/(Max-Min)+1;
	
	view->SetHighColor(100,166,222);
	
	view->FillRect(BRect(BPoint(inset+2,2),BPoint(inset+s,foo.Height()+2)));
}

void ZSlider::DrawButton(BView *view, BRect frame)
{
	BRect  work;
	float  inset;
	struct font_height 	fi;
	GetFontHeight(&fi);

	inset=frame.LeftTop().x;

	if(view->Window()->Lock())
	{	
		view->SetHighColor(222,222,222);
		view->SetLowColor(222,222,222);
		view->FillRect(frame);
	
		view->SetHighColor(128,128,128);
		view->StrokeRect(BRect(BPoint(inset,0),BPoint(inset,frame.Height())));
		view->StrokeRect(BRect(BPoint(inset,0),BPoint(frame.Width()+inset,0)));

		view->SetHighColor(255,255,255);
		view->StrokeRect(BRect(BPoint(inset+1,1),BPoint(inset+1,frame.Height()-1)));
		view->StrokeRect(BRect(BPoint(inset+1,1),BPoint(frame.Width()+inset-1,1)));

		view->SetHighColor(192,192,192);	
		view->StrokeRect(BRect(BPoint(1+inset,frame.Height()-1),BPoint(frame.Width()-1+inset,frame.Height()-1)));
		view->StrokeRect(BRect(BPoint(frame.Width()-1+inset,1),BPoint(frame.Width()-1+inset,frame.Height()-1)));

		view->SetHighColor(128,128,128);
		view->StrokeRect(BRect(BPoint(inset,frame.Height()),BPoint(frame.Width()+inset,frame.Height())));
		view->StrokeRect(BRect(BPoint(frame.Width()+inset,0),BPoint(frame.Width()+inset,frame.Height())));
		
		if(emode)
		{
			view->SetDrawingMode(B_OP_OVER);
			EDraw(view,frame,inset);
		}

		if(IsEnabled())
		{
			view->SetHighColor(0,0,0,0);
		}
		else
		{
			view->SetHighColor(100,100,100,0);
		}
		sliderhook(Str,255,Act);		
		view->MovePenTo(BPoint(((frame.Width()-view->StringWidth(Str))/2)+inset,(frame.Height()+10.0-fi.descent-fi.leading)/2));
		view->DrawString(Str);	

		view->Sync();

		view->Window()->Unlock();
		view->SetDrawingMode(B_OP_COPY);
	}
}

void ZSlider::UpdateValue()
{
	SetHighColor(222,222,222);
	FillRect(foo);
	struct font_height 	fi;
	GetFontHeight(&fi);
	
	if(emode)
	{
		float w=foo.Width();
		float s=(Act*w)/(Max-Min)+1;
			
		SetHighColor(100,166,222);
		
		FillRect(BRect(BPoint(2,2),BPoint(s,foo.Height()+2)));
		SetDrawingMode(B_OP_OVER);
	}

	if(IsEnabled())
	{
		SetHighColor(0,0,0,0);
	}
	else
	{
		SetHighColor(100,100,100,0);
	}
	sliderhook(Str,255,Act);
	MovePenTo(BPoint(((temp.Width()-StringWidth(Str))/2),(temp.Height()+10.0-fi.descent-fi.leading)/2));
	DrawString(Str);	
	Sync();
	SetDrawingMode(B_OP_COPY);
}

void ZSlider::AttachedToWindow()
{
	Bound=Bounds();
	if(Window()->Lock())
	{
		SetFont(be_plain_font);
		SetFontSize(10.0);
		SetDrawingMode(B_OP_COPY);	
		MaxStrLngth=max_c(StringWidth(MaxStr)+4,Bound.Width());	
		temp=BRect(Bound.LeftTop(),BPoint(MaxStrLngth,Bound.Height()));
		foo=BRect(Bound.LeftTop(),BPoint(MaxStrLngth-4,Bound.Height()-4));
		foo.OffsetBy(2,2);
		fontheight=be_plain_font->Size();
		Window()->Unlock();
		if(Parent())
		{
			SetViewColor(Parent()->ViewColor());
		}
	}
}

void ZSlider::Draw(BRect update)
{
	SetHighColor(ViewColor());
	SetDrawingMode(B_OP_COPY);
	if(temp.RightTop().x<update.RightBottom().x)
	{
		FillRect(BRect(temp.RightTop(),update.RightBottom()));
	}
	
	DrawButton(this,temp);

	if(IsFocus())
	{
		BRect r=Bound;	
		r.left+=2;
		r.top+=2;
		r.right-=2;
		r.bottom-=2;
		SetHighColor(66,66,66);
		StrokeRect(r,B_MIXED_COLORS);
	}
	
	MovePenTo(BPoint(MaxStrLngth+4,(Bound.Height()+be_plain_font->Size())/2));
	if(Label())DrawString(Label());
}

void ZSlider::KeyDown(const char *data, int num)
{
	switch(data[0])
	{
		case	B_FUNCTION_KEY:
		case	B_LEFT_ARROW:
		case	B_RIGHT_ARROW:
		case	's':
		case	'S':
		case	B_ESCAPE:
		case	'P':
		case	'p':
				if(Parent())Parent()->KeyDown(data,num);
				break;

		default:
				BControl::KeyDown(data,num);
				break;
	}
}

void ZSlider::MouseDown__(BPoint point)
{
	mousedown=TRUE;

	ZSliderWindow	*win;
	ZSliderView *view;
	BPoint	topedge,lowedge,oldpoint=point;
	uint32 	buttons;
	int32 	oldval=Act;
	BRect	work;
	bool	waslocked=FALSE;	
	bool 	en;

	Window()->Lock();
	en=IsEnabled();
	Window()->Unlock();

	if(en)
	{	
		notick=TRUE;
	
		oldpoint.x-=Act;
	
		if(temp.RightBottom().x<point.x) return;	
	
		Window()->Lock();
		topedge=ConvertToScreen(BPoint(0,0));
		Window()->Unlock();
		lowedge=topedge;
		lowedge.y+=Bound.Height();
		lowedge.x+=MaxStrLngth+
			((
				Min>0 ? 	
					Max>0 ? (Max-Min)		: (Min-Max) 
				: 
					Max>0 ? (abs(Min)+Max)	: (abs(Max)-Min)
			)/divisor);
		work=BRect(topedge,lowedge);
		work.OffsetBy((Min-Act)/divisor,0);

		win = new ZSliderWindow(work,this);
		win->AddChild(view = new ZSliderView(win->Bounds(),this));
		win->Show();
		do 
		{
			snooze(10000);
			Act=(point.x-oldpoint.x)*divisor;
			if(Act>Max)Act=Max;
			if(Act<Min)Act=Min;
			if(Act!=oldval)
			{
				if(crazy&&clickmsg)Invoke();
				if(win->Lock())
				{	
					view->Draw(view->Bounds());
					win->Unlock();
				}
				oldval=Act;
			}
			Window()->Lock();
			GetMouse(&point, &buttons);
			Window()->Unlock();
		}
		while ( buttons );
		
		if(Window()->Lock())
		{
			Draw(Bound);
			Window()->Unlock();
		}
		win->PostMessage(B_QUIT_REQUESTED);
		if(clickmsg)Invoke();
		
		notick=FALSE;
	}
	
	mousedown=FALSE;
}

void ZSlider::MouseDown_(ZSlider *o)
{
	o->MouseDown__(o->mousepoint);
}

void ZSlider::MouseDown(BPoint point)
{
	mousepoint = point;
	resume_thread(spawn_thread((thread_entry)MouseDown_,"ZSlider MouseDown",B_NORMAL_PRIORITY,this));
}

ZSliderWindow::ZSliderWindow(BRect frame, ZSlider *slider) : BWindow(frame, NULL, B_BORDERED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK)
{	
}

void ZSliderView::AttachedToWindow(void)
{
	Bound=Bounds();
}

ZSliderView::ZSliderView(BRect frame, ZSlider *slider) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW)
{
	Slider=slider;	
}

void ZSliderView::Draw(BRect updateRect)
{
	int32 Act;
	BRect frame;
	SetDrawingMode(B_OP_COPY);	
	Act=Slider->GetValue();
	frame=Slider->GetButton();
	SetHighColor(222,222,222);
	frame.OffsetBy(Act/Slider->divisor,0);
	FillRect(BRect(BPoint(0,0),frame.LeftBottom()));
	FillRect(BRect(frame.RightTop(),Bound.RightBottom()));
	SetFont(be_plain_font);
	SetFontSize(10.0);	
	Slider->DrawButton(this,frame);
}