#ifndef CODE_TREEVIEW_CXX
#define CODE_TREEVIEW_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: treeview.cxx,v 1.11 2001/02/11 14:12:03 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 <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "types.h"
#include <Xclasses/treeview.h>
#include "include/treeview_p.h"

tv_tree::tv_tree(void) : node(), sub()
{
	text=NULL;
	parent=NULL;
	opened=0;
	hassub=0;
	newtext=0;
	opening=0;
	closing=0;
	user=0;
	level=0;
	width=0;
	icon=NULL;
}
tv_tree::tv_tree(char *t) : node(), sub()
{
	text=NULL;
	parent=NULL;
	opened=0;
	hassub=0;
	newtext=0;
	opening=0;
	closing=0;
	user=0;
	level=0;
	width=0;
	icon=NULL;
	Text(t);
}
tv_tree::~tv_tree()
{
	if (text) free(text);
	RemoveAll();
}
void tv_tree::Text(char *t)
{
	if (text) free(text);
	text=strdup(t);
	newtext=1;
}
void tv_tree::Icon(gfx_object *g)
{
	icon=g;
}
void tv_tree::Open(void)
{
	if (sub.Count()>0)
	{
		int oopened=opened;
		opened=1;
		if (parent) parent->Open();
		if (!oopened) Update(this);
	}
}
void tv_tree::Close(void)
{
	if (sub.Count()>0)
	{
		int oopened=opened;
		opened=0;
		if (oopened) Update(this);
	}
}
void tv_tree::AddHead(tv_tree *t)
{
	int ohassub=hassub;

	sub.AddHead(t);
	SetHasSub();
	t->parent=this;
	if (opened || !ohassub) Update(this);
}
void tv_tree::AddTail(tv_tree *t)
{
	int ohassub=hassub;

	sub.AddTail(t);
	SetHasSub();
	t->parent=this;
	if (opened || !ohassub) Update(this);
}
void tv_tree::Insert(tv_tree *t,tv_tree *before)
{
	int ohassub=hassub;

	sub.Insert(t,before);
	SetHasSub();
	t->parent=this;
	if (opened || !ohassub) Update(this);
}

void tv_tree::Remove(tv_tree *t)
{
	sub.Remove(t);
	t->parent=NULL;
	if (sub.Count()==0) SetHasNoSub();
	if (opened || !hassub) Update(this);
}
void tv_tree::RemoveAll(unsigned int flags)
{
	int ohassub=hassub;
	tv_tree *t1;

	while (t1=(tv_tree*)sub.RemTail())
	{
		t1->parent=NULL;
		if (flags & RAF_DELETE) delete t1;
	}
	SetHasNoSub();
	if (opened || ohassub) Update(this);
}
void tv_tree::Update(tv_tree *t)
{
	if (parent) parent->Update(t);
}
int tv_tree::Level(void)
{
	if (level>0) return level;
	if (!parent) return 0;

	level=parent->Level()+1;
	return level;
}
// ----------------------------------------------------------------------------

treeview::treeview(void) : listview()
{
	Name("treeview");
	while (!(p=new ptreeview))
	{
		outOfMemory("treeview");
	}
	p->o=this;
	SetHook(p,(SETHOOK)&ptreeview::lvhook,0);
	ShowSelected();
	p->subbox.ParentClass(this);
}
treeview::~treeview(void)
{
	delete p;
}
ptreeview::ptreeview(void) : callable(), roottree(this), subbox()
{
	subbox.Name("subbox");
}
ptreeview::~ptreeview()
{
}

void treeview::SetRoot(tv_tree *t)
{
	p->roottree.RemoveAll();
	RemoveAll();
	if (t)
	{
		p->roottree.AddTail(t);
		p->roottree.Update(t);
	}
}

// #define HD_SELECTED     0x00000001
// #define HD_MARKED       0x00000002
// #define HD_LOCKED       0x00000004
// 
// struct hook_data
// {
//         int             version;
//         int             number;
//         int             column;
//         int             x;
//         int             y;
//         int             width;
//         int             height;
//         int             flags;
//         gadget          *drawable;
//         multilistview   *list;
//         void            *itemdata;
//         void            *userdata;
// };
int ptreeview::lvhook(struct hook_data *hd)
{
	tv_tree *t=(tv_tree*)hd->itemdata;
	t->width=hd->x+(t->Level()-1)*SUBDIST+SUBBOXSIZE;
	int r=t->width;

	XSetForeground(hd->drawable->display(),hd->drawable->gg_gc(),linecolor);

	tv_tree *par=t;
	for(int i=t->Level()-2;i>=0;i--)
	{
		if (par)
		{
			par=par->parent;
			if (par->next->next)
			{
				XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
							hd->x+i*SUBDIST+SUBBOXSIZE/2,hd->y,hd->x+i*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height-1);
				XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
							hd->x+i*SUBDIST+1+SUBBOXSIZE/2,hd->y,hd->x+i*SUBDIST+1+SUBBOXSIZE/2,hd->y+hd->height-1);
			}
		}
	}

	// obere Linie
	XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
				hd->x+(t->Level()-1)*SUBDIST+SUBBOXSIZE/2,hd->y,hd->x+(t->Level()-1)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height/2);
	XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
				hd->x+(t->Level()-1)*SUBDIST+1+SUBBOXSIZE/2,hd->y,hd->x+(t->Level()-1)*SUBDIST+1+SUBBOXSIZE/2,hd->y+hd->height/2);

	// Quer-Linie
	XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
				hd->x+(t->Level()-1)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height/2,hd->x+(t->Level()-0)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height/2);
	XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
				hd->x+(t->Level()-1)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height/2+1,hd->x+(t->Level()-0)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height/2+1);
	r+=SUBDIST;

	if (t->IsOpen())
	{ // untere Linie
		XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
					hd->x+(t->Level()-0)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height/2,hd->x+(t->Level()-0)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height-1);
		XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
					hd->x+(t->Level()-0)*SUBDIST+1+SUBBOXSIZE/2,hd->y+hd->height/2,hd->x+(t->Level()-0)*SUBDIST+1+SUBBOXSIZE/2,hd->y+hd->height-1);
	}
	if (t->next->next)
	{
		XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
					hd->x+(t->Level()-1)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height/2,hd->x+(t->Level()-1)*SUBDIST+SUBBOXSIZE/2,hd->y+hd->height-1);
		XDrawLine(hd->drawable->display(),hd->drawable->gg_win(),hd->drawable->gg_gc(),
					hd->x+(t->Level()-1)*SUBDIST+1+SUBBOXSIZE/2,hd->y+hd->height/2,hd->x+(t->Level()-1)*SUBDIST+1+SUBBOXSIZE/2,hd->y+hd->height-1);
	}

	if (t->HasSub())
	{
		if (t->IsOpen())
			subbox.draw_selected(hd->drawable,hd->x+(t->Level()-1)*SUBDIST,hd->y+(hd->height-SUBBOXSIZE)/2,SUBBOXSIZE,SUBBOXSIZE);
		else
			subbox.draw_normal(hd->drawable,hd->x+(t->Level()-1)*SUBDIST,hd->y+(hd->height-SUBBOXSIZE)/2,SUBBOXSIZE,SUBBOXSIZE);
	}
	if (t->icon)
	{
		if (t->IsOpen())
			t->icon->draw_selected(hd->drawable,hd->x+(t->Level())*SUBDIST+SUBBOXSIZE/2+1,hd->y+(hd->height-SUBBOXSIZE)/2-1,SUBBOXSIZE,SUBBOXSIZE);
		else
			t->icon->draw_normal(hd->drawable,hd->x+(t->Level())*SUBDIST+SUBBOXSIZE/2+1,hd->y+(hd->height-SUBBOXSIZE)/2-1,SUBBOXSIZE,SUBBOXSIZE);
		r+=SUBBOXSIZE+1;
	}
	return -r;
}

int ptreeview::search(tv_tree *root,tv_tree *s,int &r)
{
	int ret=0;
	tv_tree *t=(tv_tree*)root->sub.head;
	while (t->next)
	{
		r++;
		if (t==s)
		{
			ret=1;
			break;
		}
		if (t->HasSub() && t->IsOpen())
		{
			if (search(t,s,r))
			{
				ret=1;
				break;
			}
		}
		t=(tv_tree*)t->next;
	}
	return ret;
}
void treeview::Selected(tv_tree *t)
{
	t->Open();
	int s=0;
	if (p->search(&p->roottree,t,s))
		listview::Selected(s);
}
tv_tree *treeview::Selected(void)
{
	if (listview::Selected())
		return (tv_tree*)GetData(listview::Selected());
	else
		return NULL;
}

int treeview::Create(void)
{
	if (listview::Create())
	{
		p->linecolor=col_black();
		return 1;
	}
	return 0;
}
int treeview::GActivate(XEvent *ev,int why)
{
	if (why==GA_BUTTONPRESS && ev && ev->xbutton.button==1)
	{
		tv_tree *osel=Selected();
		int r=listview::GActivate(ev,why);
		int t=listview::Top();
		if (listview::Selected()>0)
		{
			if (wasfor && wasfor->Name()[0]=='l' && wasfor->Name()[1]=='i' && wasfor->Name()[4]=='f' && wasfor->Name()[5]=='i'
				&& ev->xbutton.x<((tv_tree*)GetData(listview::Selected()))->width)
			{
				if (((tv_tree*)GetData(listview::Selected()))->IsOpen())
				{
					if (!((tv_tree*)GetData(listview::Selected()))->closing)
					{
						((tv_tree*)GetData(listview::Selected()))->closing=1;
						((tv_tree*)GetData(listview::Selected()))->Close();
						((tv_tree*)GetData(listview::Selected()))->closing=0;
					}
				}
				else
				{
					if (!((tv_tree*)GetData(listview::Selected()))->opening)
					{
						((tv_tree*)GetData(listview::Selected()))->opening=1;
						((tv_tree*)GetData(listview::Selected()))->Open();
						((tv_tree*)GetData(listview::Selected()))->opening=0;
					}
				}

				int nsel=0;
				if (osel)
				{
					if (!p->search(&p->roottree,osel,nsel))
						nsel=0;
				}
				listview::Selected(nsel);
				listview::Top(t);
				r=GAR_MESSAGE;
				if (Selected()!=osel)
					r|=GAR_ACTIVE;
			}
		}
		return r;
	}
	if (why==GA_KEY)
	{
		KeySym key;

		key=LookupKeysym(ev);

		if ((key==XK_space || key==XK_Select) && listview::Selected()>0)
		{
			if (((tv_tree*)GetData(listview::Selected()))->IsOpen())
			{
				if (!((tv_tree*)GetData(listview::Selected()))->closing)
				{
					((tv_tree*)GetData(listview::Selected()))->closing=1;
					((tv_tree*)GetData(listview::Selected()))->Close();
					((tv_tree*)GetData(listview::Selected()))->closing=0;
				}
			}
			else
			{
				if (!((tv_tree*)GetData(listview::Selected()))->opening)
				{
					((tv_tree*)GetData(listview::Selected()))->opening=1;
					((tv_tree*)GetData(listview::Selected()))->Open();
					((tv_tree*)GetData(listview::Selected()))->opening=0;
				}
			}

			listview::Selected(listview::Selected());
			return GAR_MESSAGE;
		}
	}
	return listview::GActivate(ev,why);
}

void ptv_tree::Update(tv_tree *t)
{
	tv->draw(t);
}
void ptreeview::draw(tv_tree *t)
{
	int s=show(&roottree,o->Count());
	if (s<o->Count())
		o->Remove(s+1,o->Count());
}
int ptreeview::show(tv_tree *root,int oldcount,int r)
{
	tv_tree *t=(tv_tree*)root->sub.head;
	while (t->next)
	{
		r++;
		if (r>oldcount)
		{
			o->AddAtEnd(t->text,t);
			t->newtext=0;
		}
		else
		{
			if (o->GetData(r)!=t || t->newtext)
			{
				o->ChangeData(r,t);
				o->Replace(r,t->text);
				t->newtext=0;
			}
		}
		if (t->HasSub() && t->IsOpen())
		{
			r=show(t,oldcount,r);
		}
		t=(tv_tree*)t->next;
	}
	return r;
}

#endif
