#ifndef CODE_MULTILISTVIEW_CXX
#define CODE_MULTILISTVIEW_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: multilistview.cxx,v 1.20 2002/07/31 09:34:00 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/multilistview.h>
#include <Xclasses/dlist.h>
#include <Xclasses/callable.h>
#include "../include/defaults.h"
#include "../include/callback.h"
#include "include/multilistview_p.h"


void lv_moveUp(multilistview *lv)
{
	if (lv->FindNextMarked(0)!=0)
	{
		if (lv->FindNextMarked(0)>1)
		{
			int m=0;
			while(m=lv->FindNextMarked(m))
			{
				lv->MoveLine(m,m-1);
			}
		}
	}
	else if (lv->Selected()>1)
	{
		lv->MoveLine(lv->Selected(),lv->Selected()-1);
	}
}
void lv_moveDown(multilistview *lv)
{
	if (lv->FindNextMarked(0)!=0)
	{
		if (!lv->IsMarked(lv->Count()))
		{
			for(int m=lv->Count();m>0;m--)
			{
				if (lv->IsMarked(m))
					lv->MoveLine(m,m+1);
			}
		}
	}
	else if (lv->Selected() && lv->Selected()<lv->Count())
	{
		lv->MoveLine(lv->Selected(),lv->Selected()+1);
	}
}
void lv_remove(multilistview *lv,void *cl,void f(void *))
{
	class callback cb;
	int m=0;
	void *n;
	cb.set((CBCAST) f);
	if (lv->FindNextMarked(0)==0)
	{
		n=lv->GetData(lv->Selected());;
		lv->Remove(lv->Selected());
		cb.call((void*)n);
	}
	while(m=lv->FindNextMarked(0))
	{
		n=lv->GetData(m);
		lv->Remove(m);
		cb.call((void*)n);
	}
}
void lv_remove(multilistview *lv,callable *cl,void (callable::*f)(void *))
{
	class callback cb;
	int m=0;
	void *n;
	cb.set(cl,(CALLFUNCTION) f);
	if (lv->FindNextMarked(0)==0)
	{
		n=lv->GetData(lv->Selected());;
		lv->Remove(lv->Selected());
		cb.call((void*)n);
	}
	while(m=lv->FindNextMarked(0))
	{
		n=lv->GetData(m);
		lv->Remove(m);
		cb.call((void*)n);
	}
}
void lv_forMarked(multilistview *lv,void *cl,void f(void *))
{
	class callback cb;
	int m=0;
	void *n;
	cb.set((CBCAST) f);
	if (lv->FindNextMarked(0)==0)
	{
		n=lv->GetData(lv->Selected());;
		cb.call((void*)n);
	}
	while(m=lv->FindNextMarked(m))
	{
		n=lv->GetData(m);
		cb.call((void*)n);
	}
}
void lv_forMarked(multilistview *lv,callable *cl,void (callable::*f)(void *))
{
	class callback cb;
	int m=0;
	void *n;
	cb.set(cl,(CALLFUNCTION) f);
	if (lv->FindNextMarked(0)==0)
	{
		n=lv->GetData(lv->Selected());;
		cb.call((void*)n);
	}
	while(m=lv->FindNextMarked(m))
	{
		n=lv->GetData(m);
		cb.call((void*)n);
	}
}

multilistview::multilistview(): gadget(), Ddraw(this)
{
	while (!(p=new pmultilistview))
	{
		outOfMemory("multilistview");
	}
	p->o=this;
	BorderSize(2);
	KeyBorder();
	Name("multilistview");
	p->scroll.ParentClass(this);

	column_spec spec;
	spec.flags=CSF_USEWEIGHT;
	spec.weight=100;
	Columns(1,&spec);
}
multilistview::~multilistview()
{
	if (win) Free();
	RemoveAll();
	if (p) delete p;
}
pmultilistview::pmultilistview() : fcb(), items(), scroll(), glock()
{
	selmode=MD_EINFACH;
	multi=0;
	direct=0;
	spacing=0;
	scroller_width=20; //16;
	zeilen=1;
	
	top=1;
	topp=NULL;
	selected=0;
	selectedp=NULL;
	normal_selected=0;
	selectedcolumn=0;

	shift=0;
	key=0;
	hascut=0;
	hastitle=0;
	dclick=0;
	
	scroll.specialflags|=SPF_NOKEYBORDER;
	columns=0;
	specs=NULL;
	rubgc=0;
	titlesize=0;
}
pmultilistview::~pmultilistview()
{
	if (specs) delete [] specs;
	specs=NULL;
}
void multilistview::RemoveAll(void)
{
	struct lvnode *n1;
	
	while (n1=(lvnode*)p->items.RemHead())
	{
		free(n1);
	}
	for(int i=0;i<p->columns;i++)
		p->specs[i].minwidth=1;

	p->checkvars();
	p->setvars();
	p->textaus(TA_EXPOSE);
}
void multilistview::Remove(int nr)
{
	struct lvnode *n1;

	if (n1=(lvnode*)p->items.Find(nr))
	{
		p->items.Remove((node*)n1);
		free(n1);
		for(int i=0;i<p->columns;i++)
			p->specs[i].minwidth=1;
		p->checkvars();
		p->setvars();
		p->textaus(TA_EXPOSE);
	}
}
void multilistview::Remove(int start,int end)
{
	struct lvnode *n1,*n2;

	if (n1=(lvnode*)p->items.Find(start))
	{
		while (n1->base.next && start<=end)
		{
			n2=(lvnode*)n1->base.next;
			p->items.Remove((node*)n1);
			free(n1);
			start++;
			n1=n2;
		}
		for(int i=0;i<p->columns;i++)
			p->specs[i].minwidth=1;
		p->checkvars();
		p->setvars();
		p->textaus(TA_EXPOSE);
	}
}
void multilistview::Replace(int nr,char **t)
{
	struct lvnode *n1,*n2,*n3;
	
	if (n1=(lvnode *)malloc(sizeof(lvnode)+p->length(t)+p->columns*sizeof(char*)))
	{
		if (n2=(lvnode*)p->items.Find(nr))
		{
			n3=(lvnode*)n2->base.next;
			p->items.Remove((node*)n2);
			*n1=*n2;
			n1->base.text=NULL;
			p->copyColumns(n1,t);
			p->items.Insert((node*)n1,(node*)n3);
			if (p->selectedp==n2) p->selectedp=n1;
			free(n2);
			p->checkvars();
			p->setvars();
			p->textaus(TA_EXPOSELINE,nr);
		}
		else
			free(n1);
	}
}
void multilistview::Replace(int nr,int col,char *t)
{
	struct lvnode *n1,*n2,*n3;
	
	if (n2=(lvnode*)p->items.Find(nr))
	{
		n2->text[col]=t;
		if (n1=(lvnode *)malloc(sizeof(lvnode)+p->length(n2->text)+p->columns*sizeof(char*)))
		{
			n3=(lvnode*)n2->base.next;
			p->items.Remove((node*)n2);
			*n1=*n2;
			n1->base.text=NULL;
			p->copyColumns(n1,n2->text);
			p->items.Insert((node*)n1,(node*)n3);
			if (p->selectedp==n2) p->selectedp=n1;
			free(n2);
			p->checkvars();
			p->setvars();
			p->textaus(TA_EXPOSELINE,nr);
		}
	}

	
}
void multilistview::AddAtBegin(char **t)
{
	AddAtBegin(t,NULL);
}
int pmultilistview::length(char **t)
{
	int s=0;
	for(int i=0;i<columns;i++)
	{
		if (*t) s+=strlen(*t)+1; else s+=1;
		t++;
	}
	return s;
}
void pmultilistview::copyColumns(struct lvnode *n1,char **t)
{
	char *c;

	c=(char*)n1->text+columns*sizeof(char*);
	for(int i=0;i<columns;i++)
	{
		n1->text[i]=c;
		if (specs[i].cs.flags & CSF_USESHORTCUT)
		{
			n1->base.text=n1->text[i];
		}
		if (specs[i].cs.flags & CSF_AUTOSCALE)
		{
			// todo ????
		}
		if (*t)
		{
			strcpy(c,*t);
			c+=strlen(c)+1;
		}
		else
		{
			*c=0;
			c+=1;
		}
		t++;
	}
}
void multilistview::AddAtBegin(char **t,void *data)
{
	struct lvnode *n1;

	if (n1=(lvnode *)malloc(sizeof(lvnode)+p->length(t)+p->columns*sizeof(char*)))
	{
		n1->locked=0;
		n1->marked=0;
		n1->data=data;
		n1->base.text=NULL;
		p->copyColumns(n1,t);
		p->items.AddHead((node*)n1);
		p->checkvars();
		p->setvars();
		p->textaus(TA_EXPOSE);
	}
}
void multilistview::AddAtEnd(char **t)
{
	AddAtEnd(t,NULL);
}
void multilistview::AddAtEnd(char **t,void *data)
{
	struct lvnode *n1;
	
	if (n1=(lvnode *)malloc(sizeof(lvnode)+p->length(t)+p->columns*sizeof(char*)))
	{
		n1->locked=0;
		n1->marked=0;
		n1->data=data;
		n1->base.text=NULL;
		p->copyColumns(n1,t);
		p->items.AddTail((node*)n1);
		p->checkvars();
		p->setvars();
		p->textaus(TA_EXPOSELINE,n1);
	}
}
void multilistview::AddBefore(int nr,char **t)
{
	AddBefore(nr,t,NULL);
}
void multilistview::AddBefore(int nr,char **t,void *data)
{
	struct lvnode *n1,*n2;

	if (!(n2=(lvnode*)p->items.Find(nr)))
	{
		n2=(lvnode*)p->items.tail->next;
	}
	if (n2 && (n1=(lvnode *)malloc(sizeof(lvnode)+p->length(t)+p->columns*sizeof(char*))))
	{
		n1->locked=0;
		n1->marked=0;
		n1->data=data;
		n1->base.text=NULL;
		p->copyColumns(n1,t);
		p->items.Insert((node*)n1,(node*)n2);
		p->checkvars();
		p->setvars();
		p->textaus(TA_EXPOSEBELOW,n1);
	}
}
void multilistview::MoveLine(int from,int to)
{
	struct lvnode *n1,*n2;

	if (!(n1=(lvnode*)p->items.Find(from)))
	{
		return;
	}
	p->items.Remove((node*)n1);
	if (!(n2=(lvnode*)p->items.Find(to)))
	{
		n2=(lvnode*)p->items.tail->next;
	}
	p->items.Insert((node*)n1,(node*)n2);
	p->checkvars();
	p->setvars();
	p->textaus(TA_EXPOSE);
}
int multilistview::Count(void)
{
	return p->items.Count();
}
void *multilistview::GetData(int nr)
{
	struct lvnode *n1;

	if (n1=(lvnode*)p->items.Find(nr))
	{
		return n1->data;
	}
	return NULL;
}
void multilistview::ChangeData(int nr,void *data)
{
	struct lvnode *n1;

	if (n1=(lvnode*)p->items.Find(nr))
	{
		n1->data=data;
	}
}

void multilistview::LockItem(int i)
{
	struct lvnode *n;
	
	if (n=(lvnode*)p->items.Find(i))
	{
		if (!n->locked)
		{
			n->locked=1;
			if (p->selectedp==n)
			{ // keine tolle Lösung, sonst wird in keiner der Funktionen selected geändert
				p->selectedp=NULL;
				p->selected=0;
			}
			p->textaus(TA_EXPOSELINE,i);
		}
	}
}
void multilistview::UnlockItem(int i)
{
	struct lvnode *n;
	
	if (n=(lvnode*)p->items.Find(i))
	{
		if (n->locked)
		{
			n->locked=0;
			p->textaus(TA_EXPOSELINE,i);
		}
	}
}
void multilistview::MarkItem(int i)
{
	struct lvnode *n;
	
	if (n=(lvnode*)p->items.Find(i))
	{
		if (!n->marked)
		{
			n->marked=1;
			p->textaus(TA_EXPOSELINE,i);
		}
	}
}
void multilistview::UnmarkItem(int i)
{
	struct lvnode *n;
	
	if (n=(lvnode*)p->items.Find(i))
	{
		if (n->marked)
		{
			n->marked=0;
			p->textaus(TA_EXPOSELINE,i);
		}
	}
}
int multilistview::FindNextMarked(int m)
{
	struct lvnode *n=NULL;
	int k=0;
	
	if (m==0)
	{
		n=(lvnode*)p->items.head;
	}
	else if (n=(lvnode*)p->items.Find(m))
	{
		n=(lvnode*)n->base.next;
	}
	while (n && n->base.next && !n->marked)
	{
		n=(lvnode*)n->base.next;
	}
	if (n && n->base.next) k=p->items.Find((node*)n);
	return k;
}
int multilistview::IsMarked(int i)
{
	int m=0;
	struct lvnode *n;
	
	if (n=(lvnode*)p->items.Find(i))
	{
		m=n->marked;
	}
	
	return m;
}
void multilistview::ShowSelected(void)
{
	p->selmode=MD_SELECTED;
}
void multilistview::ReadOnly(void)
{
	p->selmode=MD_READONLY;
}
void multilistview::Spacing(int s)
{
	p->spacing=s;
}
void multilistview::MultiSelect(void)
{
	p->multi=1;
}
void multilistview::DirectMulti(void)
{
	p->direct=1;
}
void multilistview::SingleSelect(void)
{
	p->multi=0;
	p->direct=0;
}

int multilistview::visible_width(void)
{
	return width-p->scroller_width-RealSize()*2-4;
}
int multilistview::visible_height(void)
{
	return p->specs[0].innen.gg_height();
}

void multilistview::ColumnConfigure(int c,struct column_spec *spec)
{
	c--;
	p->specs[c].minwidth=1;
	if (spec->flags & CSF_USEWEIGHT)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags & ~CSF_USEWIDTH | CSF_USEWEIGHT;
		p->specs[c].cs.weight=spec->weight;
		if (win) Resize(width,height);
	}
	if (spec->flags & CSF_USEWIDTH)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags & ~CSF_USEWEIGHT | CSF_USEWIDTH;
		p->specs[c].cs.width=spec->width;
		if (win) Resize(width,height);
	}
	if (spec->flags & CSF_SELECTABLE)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags & ~CSF_NOTSELECTABLE | CSF_SELECTABLE;
	}
	if (spec->flags & CSF_NOTSELECTABLE)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags & ~CSF_SELECTABLE | CSF_NOTSELECTABLE;
	}
	if (spec->flags & CSF_USESHORTCUT)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags | CSF_USESHORTCUT;
		p->hascut=1;
	}
	if (spec->flags & CSF_HOOK)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags | CSF_HOOK;
		p->specs[c].cs.hookclass=spec->hookclass;
		if (spec->hookclass)
			p->specs[c].cs.objhook=spec->objhook;
		else
			p->specs[c].cs.hook=spec->hook;
			
		p->specs[c].cs.userdata=spec->userdata;
		if (!p->specs[c].cs.hook && !p->specs[c].cs.objhook) p->specs[c].cs.flags=p->specs[c].cs.flags & ~CSF_HOOK;
	}
	if (spec->flags & CSF_ADJUST)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags | CSF_ADJUST;
		p->specs[c].cs.adjust=spec->adjust;
	}
	if (spec->flags & CSF_TITLE)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags | CSF_TITLE;
		p->specs[c].cs.title=spec->title;
		p->hastitle=1;
		p->specs[c].title.Text(spec->title);
	}
	if (spec->flags & CSF_SCALEABLE && c<p->columns-1)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags | CSF_SCALEABLE;
	}
	if (spec->flags & CSF_AUTOSCALE && c<p->columns-1)
	{
		p->specs[c].cs.flags=p->specs[c].cs.flags | CSF_AUTOSCALE;
	}
}
void multilistview::Columns(int c,struct column_spec spec[])
{
	if (c>0)
	{
		char buf[30];
		p->columns=c;

		if (p->specs) delete [] p->specs;

 		p->specs=new pspecs[c];
 		for(int i=0;i<c;i++)
 		{
			p->specs[i].x=0;
			p->specs[i].fixwidth=-1;
 			p->specs[i].cs.weight=10/c;
 			p->specs[i].cs.width=0;
 			p->specs[i].cs.adjust=ADJUST_LEFT;
 			p->specs[i].cs.hookclass=NULL;
 			p->specs[i].cs.hook=NULL;
 			p->specs[i].cs.objhook=0;
 			p->specs[i].cs.userdata=NULL;
 			p->specs[i].cs.flags=CSF_ADJUST;
 			ColumnConfigure(i+1,&spec[i]);
 
  			p->specs[i].innen.ParentClass(this);
  			sprintf(buf,"listfield_%d",i+1);
  			p->specs[i].innen.Name(buf);

  			p->specs[i].button.ParentClass(this);
  			sprintf(buf,"listscalebutton_%d",i+1);
  			p->specs[i].button.Name(buf);

  			p->specs[i].title.ParentClass(this);
  			sprintf(buf,"listtitle_%d",i+1);
  			p->specs[i].title.Name(buf);
			p->specs[i].title.Callback(p,(CALLBACK)&pmultilistview::title_callback);
 		}
	}
}

int pmultilistview::columnsize(int w,int col,int inRecursion)
{
	register int s=w;
	if (specs[col].fixwidth>=0)
	{
		s=specs[col].fixwidth;
		if (s<=0) s=o->RealSize()*2+4;
	}
	else if (specs[col].cs.flags & CSF_AUTOSCALE)
	{
		s=specs[col].minwidth;
		if (specs[col].cs.flags & CSF_SCALEABLE)
		{
			s+=SCALESIZE;
		}
		if (specs[col].cs.flags & CSF_USEWIDTH)
		{
			int sub=specs[col].cs.width*(fontheight+spacing)/10;
			if (s>sub) s=sub;
		}
		else if (specs[col].cs.flags & CSF_USEWEIGHT)
		{
			int s2=(w-scroller_width-o->RealSize()*2-4)*specs[col].cs.weight;
			s2=s2/100;	// required! without eg. -2600/100 = 42949646 !!!
			if (s2<=0) s2=o->RealSize()*2+4;
			if (s>s2) s=s2;
		}
		if (s>(w-scroller_width-o->RealSize()*2-4)-columns) s=(w-scroller_width-o->RealSize()*2-4)-columns;
		if (s<=0) s=1;
	}
	else if (specs[col].cs.flags & CSF_USEWIDTH)
	{
		s=specs[col].cs.width*(fontheight+spacing)/10;
		if (s<=0) s=1;
	}
	else if (specs[col].cs.flags & CSF_USEWEIGHT)
	{
		for(int i=0;i<columns;i++)
		{
			if ((specs[i].cs.flags & CSF_USEWIDTH) || (specs[col].cs.flags & CSF_AUTOSCALE))
				w-=columnsize(w,i,1);
		}
		if (w>0)
		{
			s=(w-scroller_width-o->RealSize()*2-4)*specs[col].cs.weight;
			s=s/100;	// required! without eg. -2600/100 = 42949646 !!!
			if (s<=0) s=o->RealSize()*2+4;
		}
		else
			s=o->RealSize()*2+4;
	}
	else
	{
		s=(w-scroller_width-o->RealSize()*2-4)*(100/columns)/100;
		if (s<=0) s=o->RealSize()*2+4;
	}
	if (specs[col].cs.flags & CSF_SCALEABLE)
	{
		s-=SCALESIZE;
		if (s<=0) s=1;
	}
	if (!inRecursion && (specs[col].x+s)>(o->width-scroller_width-o->RealSize()-columns*2))
	{
		s=o->width-scroller_width-o->RealSize()-columns*2-specs[col].x;
		if (s<=0) s=1;
	}
	return s;
}
int pmultilistview::bgcol(int col)
{
	if (col>=0)
		if (selmode==MD_READONLY)
			return specs[col].innen.col_background();
		else
			return specs[col].innen.col_shinebackground();
	else
		if (selmode==MD_READONLY)
			return o->col_background();
		else
			return o->col_shinebackground();
}

int multilistview::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);

		if (!setlimit)
		{
			if (p->hastitle)
			{
				for(int i=0;i<p->columns;i++)
				{
					p->specs[i].title.GSetLimits();
					if (p->specs[i].title.gg_minheight()>p->titlesize)
						p->titlesize=p->specs[i].title.gg_minheight();
				}
			}
		}
    	Mode(GM_ONEEXPOSE|GM_KEY);

		p->fontheight=gg_font()->ascent+gg_font()->descent;
		int x=RealSize()+2;
		char *res;
		for(int i=0;i<p->columns;i++)
		{
			if (res=p->specs[i].innen.GetResource("weight"))
			{
				p->specs[i].cs.weight=atoi(res);
			}
			res=p->specs[i].innen.GetResource("width");
			if (res)
				p->specs[i].fixwidth=atoi(res);

			if (x>width) x=width-p->scroller_width-RealSize()-p->columns*2+i;

			int w=p->columnsize(width,i)-SCALESUB(i);
			if (w<=0) w=1;
			if (i==p->columns-1)
				w=width-p->scroller_width-RealSize()-x;

			p->specs[i].x=x;
			p->specs[i].innen.Dimensions(win,x,RealSize()+1+p->titlesize,w,height-RealSize()*2-2-p->titlesize);
    		p->specs[i].innen.Background(p->bgcol(i));
    		p->specs[i].innen.CopyFont(this);
	    	if (!p->specs[i].innen.Create())
    		{
	    		for(int j=0;j<i;j++)
	    		{
		    		if (p->hastitle) p->specs[j].title.Free();
		    		p->specs[j].button.Free();
		    		p->specs[j].innen.Free();
		    	}
    			gadget::Free();
    			return 0;
	    	}
			if (p->specs[i].cs.flags & CSF_SCALEABLE)
			{
				p->specs[i].button.Dimensions(win,x+p->columnsize(width,i)-SCALESIZE,RealSize()+1/*+p->titlesize*/,SCALESIZE,height-RealSize()*2-2/*-p->titlesize*/);
	    		p->specs[i].button.Background(p->bgcol(i));
		    	if (!p->specs[i].button.Create())
    			{
	    			if (p->hastitle) p->specs[i].title.Free();
	    			p->specs[i].innen.Free();
		    		for(int j=0;j<i;j++)
		    		{
			    		if (p->hastitle) p->specs[j].title.Free();
			    		p->specs[j].button.Free();
		    			p->specs[j].innen.Free();
		    		}
    				gadget::Free();
	    			return 0;
		    	}
		    	p->specs[i].button.Mode(GM_ONEEXPOSE|GM_BUTTON1|GM_MOUSEMOVE|GM_MINIMALMOVE);
	    	   	p->specs[i].button.SendEventTo(this);
	    	   	p->specs[i].button.ID(i+SCALEBASE);
	    	   	p->specs[i].button.MCursor(XC_sb_h_double_arrow);
			}
			if (p->hastitle)
			{
				int w=p->columnsize(width,i)-SCALESUB(i)/2-SCALEADD(i)/2;
				if (w<=0) w=1;

				p->specs[i].title.bgcol=p->bgcol(i);
				p->specs[i].title.Dimensions(win,x+SCALEADD(i)/2,RealSize(),w,p->titlesize);
		    	if (!p->specs[i].title.Create())
    			{
	    			p->specs[i].button.Free();
	    			p->specs[i].innen.Free();
		    		for(int j=0;j<i;j++)
		    		{
			    		p->specs[j].title.Free();
			    		p->specs[j].button.Free();
		    			p->specs[j].innen.Free();
		    		}
    				gadget::Free();
	    			return 0;
		    	}
			}
			x+=p->columnsize(width,i);
    		if (p->selmode==MD_READONLY)
    		{
	       		p->specs[i].innen.Mode(GM_ONEEXPOSE);
	    	}
    		else
    		{
	       		p->specs[i].innen.Mode(GM_BUTTON1|(defaults.listview_multibutton==right?GM_BUTTON3:GM_BUTTON2)|GM_MOUSEMOVE|GM_ONEEXPOSE|GM_ACTIVETICK);
			}
    	   	p->specs[i].innen.SendEventTo(this);
    	   	p->specs[i].innen.ID(i+1);
		}
		p->zeilen=p->specs[0].innen.gg_height()/(p->fontheight+p->spacing);
		p->checkvars();
		p->setvars();

		p->scroll.Dimensions(win,width-p->scroller_width,0,p->scroller_width,height);
		p->scroll.Arrows();
    	p->scroll.CopyFont(this);
    	if (!p->scroll.Create())
    	{
    		for(int i=0;i<p->columns;i++)
	    		p->specs[i].innen.Free();
    		gadget::Free();
    		return 0;
    	}
       	p->scroll.SendEventTo(this);
//		Resize(width,height);
		return 1;
    } 
    return 0;
}
void multilistview::Free(void)
{
	for(int i=0;i<p->columns;i++)
	{
   		if (p->hastitle) p->specs[i].title.Free();
		p->specs[i].button.Free();
		p->specs[i].innen.Free();
	}
	p->scroll.Free();
	if (p->rubgc)
	{
		XFreeGC(display(),p->rubgc);
		p->rubgc=0;
	}
	gadget::Free();
}

void multilistview::Resize(int w,int h)
{
	gadget::Resize(w,h);
	p->scroll.Move(w-p->scroller_width,0);
	p->scroll.Resize(p->scroller_width,h);
	int x=RealSize()+2;
	for(int i=0;i<p->columns;i++)
	{
		if (x>width) x=width-p->scroller_width-RealSize()-p->columns*2+i;
		p->specs[i].x=x;
		p->specs[i].innen.Move(x,RealSize()+1+p->titlesize);
		if (i==p->columns-1) // && (p->specs[i].cs.flags & CSF_USEWEIGHT || p->specs[i].cs.flags & CSF_AUTOSCALE))
		{
			p->specs[i].innen.Resize(w-p->scroller_width-RealSize()-x,h-RealSize()*2-2-p->titlesize);
		}
		else
		{
			int tw=p->columnsize(w,i)-SCALESUB(i);
			if (tw<=0) tw=1;
			p->specs[i].innen.Resize(tw,h-RealSize()*2-2-p->titlesize);
		}
		if (p->specs[i].cs.flags & CSF_SCALEABLE)
		{
			p->specs[i].button.Move(x+p->columnsize(w,i)-SCALESUB(i),RealSize()+1);//+p->titlesize);
			p->specs[i].button.Resize(SCALESIZE,h-RealSize()*2-2); //-p->titlesize);
		}
		if (p->hastitle)
		{
			p->specs[i].title.Move(x+SCALEADD(i)/2,RealSize());
			if (i==p->columns-1) // && (p->specs[i].cs.flags & CSF_USEWEIGHT || p->specs[i].cs.flags & CSF_AUTOSCALE))
				p->specs[i].title.Resize(w-multilistview::p->scroller_width-RealSize()-x,multilistview::p->titlesize);
			else
			{
				int tw=multilistview::p->columnsize(w,i)-SCALESUB(i)/2-SCALEADD(i)/2;
				if (tw<=0) tw=1;
				p->specs[i].title.Resize(tw,multilistview::p->titlesize);
			}
		}
		x+=p->columnsize(w,i);
	}
	p->zeilen=p->specs[0].innen.gg_height()/(p->fontheight+p->spacing);
	p->setvars();
	Top(p->scroll.Position()+1);
}
void multilistview::Lock(void)
{
	gadget::Lock();
	for(int i=0;i<p->columns;i++)
	{
		p->specs[i].innen.Lock();
		p->specs[i].button.Lock();
	}
	p->scroll.Lock();
}
void multilistview::Unlock(void)
{
	gadget::Unlock();
	for(int i=0;i<p->columns;i++)
	{
		p->specs[i].innen.Unlock();
		p->specs[i].button.Unlock();
	}
	p->scroll.Unlock();
}
void multilistview::Block(int b)
{
	gadget::Block(b);
	for(int i=0;i<p->columns;i++)
	{
		p->specs[i].innen.Block(b);
		p->specs[i].button.Block(b);
	}
	p->scroll.Block(b);
}
void multilistview::Unblock(int b)
{
	gadget::Unblock(b);
	for(int i=0;i<p->columns;i++)
	{
		p->specs[i].innen.Unblock(b);
		p->specs[i].button.Unblock(b);
	}
	p->scroll.Unblock(b);
}

void multilistview::GExpose(XEvent *event)
{
	if (wasfor && wasfor->Name()[0]!='l' && wasfor->Name()[1]!='i')
	{
		wasfor->GExpose(event);
	}
	else
	{
		if (wasfor && wasfor->Name()[0]=='l' && wasfor->Name()[1]=='i' && wasfor->Name()[4]=='s' && wasfor->Name()[5]=='c')
		{
			XSetForeground(display(),wasfor->gg_gc(),wasfor->col_shine());
			XDrawLine(display(),wasfor->gg_win(),wasfor->gg_gc(),2,p->titlesize,2,wasfor->gg_height());
			XSetForeground(display(),wasfor->gg_gc(),wasfor->col_shadow());
			XDrawLine(display(),wasfor->gg_win(),wasfor->gg_gc(),3,p->titlesize,3,wasfor->gg_height());
		}
		else if (wasfor && wasfor->Name()[0]=='l' && wasfor->Name()[1]=='i')
		{
			if (event && (event->type==NoExpose || event->type==GraphicsExpose))
			{
				p->textaus(TA_EVENT,event);
			}
			else
			{
				if (locked)
				{
					for(int i=0;i<p->columns;i++)
	 					p->glock.draw_locked(&p->specs[i].innen,0,0,p->specs[i].innen.gg_width(),p->specs[i].innen.gg_height());
				}
				if (!event && !locked)
				{
					for(int i=0;i<p->columns;i++)
						XClearWindow(display(),p->specs[i].innen.gg_win());
				}
				p->textaus(TA_EXPOSE,event);

			}
		}
		else
		{
			XSetForeground(display(),gc,p->bgcol());
			XFillRectangle(display(),win,gc,0,0,width-p->scroller_width,height);
			borderDown(this,0,0,width-p->scroller_width,height);
			if (p->key) bordermark(this,0,0,width-p->scroller_width,height); else borderunmark(this,0,0,width-p->scroller_width,height);
		}
	}
}
static int moved;
int multilistview::GActivate(XEvent *event,int why)
{
	int r=0;
	if (wasfor && why!=GA_KEY && wasfor==&p->scroll)
	{
		r=wasfor->GActivate(event,why) & GAR_ACTIVE;
		
		if (p->scroll.Position()!=p->top-1)
		{
			Top(p->scroll.Position()+1);
		}
		return r;
	}
	if (why==GA_KEY)
	{ // Cursor up/down, Return
		int c;
		char taste[5];
		KeySym key;

		active&=~GA_DCLICK;

		int dummy;
		c=LookupString(event,taste,sizeof(taste),key,dummy);

		switch (key)
		{
			case XK_Return:
			case XK_KP_Enter: // select
				r=GAR_ACTIVE;
				if (p->selmode==MD_EINFACH)
				{
					p->normal_selected=Selected();
					Selected(0);
				}
				else
				{
					active|=GA_DCLICK;
				}
				break;
			case XK_Up:
				if (p->multi)
				{
					if (event->xkey.state&ShiftMask)
					{
						if (p->selectedp)
						{
							p->selectedp->marked=!p->selectedp->marked;
							p->textaus(TA_SELNEW);
						}
					}
				}
				p->Selected(Selected()-1);
				r=GAR_ACTIVE|GAR_MESSAGE;
				break;
			case XK_Down:
				if (p->multi)
				{
					if (event->xkey.state&ShiftMask)
					{
						if (p->selectedp)
						{
							p->selectedp->marked=!p->selectedp->marked;
							p->textaus(TA_SELNEW);
						}
					}
				}
				p->Selected(Selected()+1);
				r=GAR_ACTIVE|GAR_MESSAGE;
				break;
			case XK_Page_Up:
				p->Selected(Selected()-p->zeilen);
				r=GAR_ACTIVE|GAR_MESSAGE;
				break;
			case XK_Page_Down:
				p->Selected(Selected()+p->zeilen);
				r=GAR_ACTIVE|GAR_MESSAGE;
				break;
			case XK_Home:
				p->Selected(1);
				r=GAR_ACTIVE|GAR_MESSAGE;
				break;
			case XK_End:
				p->Selected(p->items.Count());
				r=GAR_ACTIVE|GAR_MESSAGE;
				break;
			case XK_space:
			case XK_Select:
				r=0;
				if (p->multi)
				{
					if (p->selectedp)
					{
						p->selectedp->marked=!p->selectedp->marked;
						p->textaus(TA_SELNEW);
						r=GAR_MESSAGE;
					}
				}
				break;
			default:
				{
					int done=0;
					if (c>0 && p->hascut && !(event->xkey.state & ControlMask) && !(event->xkey.state & Mod1Mask))
					{
//						struct lvnode *n=(lvnode*)p->items.head;
						struct lvnode *n=p->selectedp;
						if (n)
						{
							n=(lvnode*)p->selectedp->base.next;
						}
						if (!n || !n->base.next)
						{
							n=(lvnode*)p->items.head;
							done=1;
						}
						while (n->base.next && done<2)
						{
							if (n->base.text && tolower(n->base.text[0])==tolower(taste[0]))
							{
								if (!n->locked)
								{
									p->Selected(p->items.Find((node*)n));
									done=2;
									r=GAR_ACTIVE|GAR_MESSAGE;
								}
							}
							n=(lvnode*)n->base.next;
							if (!n->base.next && done==0)
							{
								done=1;
								n=(lvnode*)p->items.head;
							}
						}
					}
					if (!done || !strlen(taste) || (unsigned)taste[0]<32)
					{
						if (p->hascut) event->xkey.state&=~Mod1Mask;
						DefaultKeyHandler(event);
					}
				}
				break;
		}
		if (p->selmode==MD_READONLY) r=0;
		if ((p->selmode==MD_EINFACH)&&(r & GAR_ACTIVE)&&(r & GAR_MESSAGE)) r=GAR_MESSAGE;
		return r;
	}

	if (why==GA_BUTTONPRESS)
	{
		int b;
		struct lvnode *n;
		gadget *ow=wasfor;
		moved=0;

		ActivateKey();
		wasfor=ow;
		if (wasfor && wasfor->Name()[0]=='l' && wasfor->Name()[1]=='i' && wasfor->Name()[4]=='s' && wasfor->Name()[5]=='c')
		{
			p->rubpos=p->columnpos(wasfor->ID()-SCALEBASE)+RealSize()-1;
			p->draw();
			r=GAR_ACTIVE;
		}
		else if (wasfor && wasfor->ID()>=1 && (p->specs[wasfor->ID()-1].cs.flags & CSF_SELECTABLE))
		{
			b=event->xbutton.y/(p->fontheight+p->spacing)+p->top;
			if (b>p->items.Count()) b=p->items.Count();
			if (b>=p->top+p->zeilen) b=p->top+p->zeilen-1;
			p->selectedcolumn=wasfor->ID()-1;

			if (b)
				n=(lvnode*)p->items.Find(b);
			else
				n=NULL;
			if (n)
			{
				if (p->multi)
				{
					if ((defaults.listview_multiblockkey==shift && (event->xbutton.state&ShiftMask))
						|| (defaults.listview_multiblockkey==control && (event->xbutton.state&ControlMask))
						|| (defaults.listview_multiblockkey==alt && (event->xbutton.state&Mod1Mask))
						)
					{
						int newsel=p->items.Find((node*)n);
						lvnode *nn=(lvnode*)p->items.head;
						p->shift=1;
						p->setmark=1;

						while (nn->base.next)
						{
							nn->marked=0;
							nn=(lvnode*)nn->base.next;
						}
						for(int i=MIN(newsel,p->selected);i<=MAX(newsel,p->selected);i++)
						{
							struct lvnode *n=(lvnode*)p->items.Find(i);
							if (n && !n->locked)
							{
								n->marked=p->setmark;
							}
						}
						p->textaus(TA_EXPOSE);
					}
					else if (p->direct
						|| (defaults.listview_multikey==shift && (event->xbutton.state&ShiftMask))
						|| (defaults.listview_multikey==control && (event->xbutton.state&ControlMask))
						|| (defaults.listview_multikey==alt && (event->xbutton.state&Mod1Mask))
						|| (defaults.listview_multibutton==middle && event->xbutton.button==2)
						|| (defaults.listview_multibutton==right && event->xbutton.button==3)
						)
					{
						p->shift=1;
						if (!n->locked)
						{
							if (n->marked) p->setmark=0; else p->setmark=1;
							n->marked=p->setmark;
						}
						else p->setmark=1;
					}
					else
					{
						p->shift=0;
					}
				}
				if (!n->locked)
				{
					if (b!=p->selected) r|=GAR_MESSAGE;
					p->selected=b;
					p->textaus(TA_SELECT);
				}
			}
			p->above=0;
			p->below=0;
			if (n) r|=GAR_ACTIVE;
		}
		return r;
	}
	if (why==GA_TICK)
	{
		int os=p->selected;
		if (p->above)
		{
			p->top--;
			if (p->top<1) p->top=1;
			p->scroll.Position(p->top-1);
			p->textaus(TA_SCROLL);
			p->Selected(p->top);
			if (p->shift)
			{
				for(int i=MIN(os,p->selected);i<=MAX(os,p->selected);i++)
				{
					struct lvnode *n=(lvnode*)p->items.Find(i);
					if (n && !n->locked)
					{
						n->marked=p->setmark;
					}
				}
			}
			p->textaus(TA_SELECT);
		}
		if (p->below)
		{
			p->top++;
			if (p->top>p->items.Count()-p->zeilen+1) p->top=p->items.Count()-p->zeilen+1;
			if (p->top<1) p->top=1;
			p->scroll.Position(p->top-1);
			p->textaus(TA_SCROLL);
			p->Selected(p->top+p->zeilen-1);
			if (p->shift)
			{
				for(int i=MIN(os,p->selected);i<=MAX(os,p->selected);i++)
				{
					struct lvnode *n=(lvnode*)p->items.Find(i);
					if (n && !n->locked)
					{
						n->marked=p->setmark;
					}
				}
			}
			p->textaus(TA_SELECT);
		}
		return (os!=p->selected)?GAR_MESSAGE:0;
	}

	return 0;
}
int multilistview::GSelected(XEvent *event,int why,int wasabove)
{
	if (wasfor && why!=GA_KEY && wasfor->Name()[0]!='l' && wasfor->Name()[1]!='i')
	{
		wasfor->GSelected(event,why,wasabove);
		
		if (p->scroll.Position()!=p->top-1)
		{
			Top(p->scroll.Position()+1);
		}
		return 0;
	}
	if (why==GA_KEY)
	{
		p->dclick=0;
		return (Selected()>0)?GSR_SELECTION|GSR_MESSAGE:0;
	}
	if (why==GA_BUTTONPRESS)
	{
//		if (wasfor) active|=wasfor->gg_active()&GA_DCLICK;
		if (active & GA_DCLICK)
		{
			if (p->dclick)
			{
				active&=~GA_DCLICK;
				p->dclick=0;
			}
			else
				p->dclick=1;
		}
		else
			p->dclick=0;

		if (wasfor && wasfor->Name()[0]=='l' && wasfor->Name()[1]=='i' && wasfor->Name()[4]=='s' && wasfor->Name()[5]=='c')
		{
			p->draw();
			int dw=p->rubpos-p->columnpos(wasfor->ID()-SCALEBASE);
			p->setWidthOf(wasfor->ID()-SCALEBASE,dw+SCALESIZE/2+1);
			if (wasfor->ID()-SCALEBASE+1<p->columns)
				p->setWidthOf(wasfor->ID()-SCALEBASE+1,-dw+SCALESIZE+2);
		}
		else
		{
			int b;
			struct lvnode *n;

			if (p->selmode==MD_EINFACH)
			{
				p->normal_selected=p->selected;
				Selected(0);
			}
			p->shift=0;

			b=event->xbutton.y/(p->fontheight+p->spacing)+p->top;
			if (b>p->items.Count()) b=p->items.Count();

			n=(lvnode*)p->items.Find(b);
			if ((n && !n->locked) || moved)
				return GSR_SELECTION|GSR_MESSAGE;
		}
	}
	return 0;
}
void multilistview::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor && reason!=GB_KEY && wasfor->Name()[0]!='l' && wasfor->Name()[1]!='i')
	{
		wasfor->GActionPress(mx,my,rx,ry,state,button,reason);
	}
	else if (reason==GB_KEY)
	{
		p->key=1;
		bordermark(this,0,0,width-p->scroller_width,height);
	}
}
void multilistview::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor && reason!=GB_KEY && wasfor->Name()[0]!='l' && wasfor->Name()[1]!='i')
	{
		wasfor->GActionRelease(mx,my,rx,ry,state,button,reason);
	}
	else if (reason==GB_KEY)
	{
		p->key=0;
		borderunmark(this,0,0,width-p->scroller_width,height);
		if (p->selmode==MD_EINFACH)
		{
			Selected(0);
		}
	}
}

int multilistview::GMouseMove(int mx,int my,int rx,int ry,unsigned int state,int button)
{
	int r=0;
	if (wasfor && wasfor->Name()[0]!='l' && wasfor->Name()[1]!='i')
	{
		wasfor->GMouseMove(mx,my,rx,ry,state,button);
		if (p->scroll.Position()!=p->top-1)
		{
			Top(p->scroll.Position()+1);
		}
	}
	else
	{
		if (wasfor && wasfor->Name()[0]=='l' && wasfor->Name()[1]=='i' && wasfor->Name()[4]=='s' && wasfor->Name()[5]=='c')
		{
			int x=p->columnpos(wasfor->ID()-SCALEBASE)+mx;
			if (x>=p->columnpos(wasfor->ID()-SCALEBASE-1) && (x<=p->columnpos(wasfor->ID()-SCALEBASE+1) || wasfor->ID()-SCALEBASE+2==p->columns) && x<width-p->scroller_width-RealSize()-4)
			{
				p->draw();
				p->rubpos=x+RealSize()-SCALESIZE/2;
				p->draw();
			}
		}
		else
		{
			p->above=0;
			p->below=0;
			if (my<0)
			{
				p->above=1;
//				GActivate(NULL,GA_TICK);
			}
			else if (my>p->zeilen*(p->fontheight+p->spacing))
			{
				p->below=1;
//				GActivate(NULL,GA_TICK);
			}
			else
			{
				int b;
				struct lvnode *n;

				b=my/(p->fontheight+p->spacing)+p->top;
				if (b>p->items.Count()) b=p->items.Count();

				n=(lvnode*)p->items.Find(b);
				if (!n->locked)
				{
					if (p->selected!=b) moved=1;
					if (p->shift && moved)
					{
						int s=p->selected;
						for(int i=MIN(b,s);i<=MAX(b,s);i++)
						{
							struct lvnode *n=(lvnode*)p->items.Find(i);
							if (n && !n->locked)
							{
								n->marked=p->setmark;
								p->selected=i;
								p->textaus(TA_SELECT);
							}
						}
					}
					if (b!=p->selected) r=GSR_MESSAGE;
					p->selected=b;
					p->textaus(TA_SELECT);
				}
			}
		}
	}
	return r;
}
void multilistview::GSetLimits(void)
{
	setlimit=1;
	p->fontheight=gg_font()->ascent+gg_font()->descent;
	p->scroll.GSetLimits();

	minwidth=p->scroller_width+RealSize()*2;
	for(int i=0;i<p->columns;i++)
	{
		if (p->hastitle)
		{
			p->specs[i].title.GSetLimits();
			if (p->specs[i].title.gg_minheight()>p->titlesize)
				p->titlesize=p->specs[i].title.gg_minheight();
		}
		minwidth+=p->columnsize(4,i)+p->fontheight;
	}
	minheight=(gg_font()->ascent+gg_font()->descent+p->spacing+2)*2+RealSize()*2+p->titlesize;
	if (p->scroll.gg_minheight()>minheight) minheight=p->scroll.gg_minheight();
}

void multilistview::Top(int t)
{
	if (t>p->items.Count()-p->zeilen+1) t=p->items.Count()-p->zeilen+1;
	if (t<1) t=1;
	if (t!=p->top)
	{
		p->top=t;
		p->scroll.Position(p->top-1);
		p->textaus(TA_SCROLL);
	}
}
int multilistview::Top(void)
{
	return p->top;
}

void pmultilistview::Selected(int t)
{
	if (t<1) t=1;
	o->Selected(t);
}
#define LINES	0
void multilistview::Selected(int t)
{
	if (p->selmode==MD_READONLY)
	{
		Top(t);
		return;
	}
	if (t<0) t=0;
	if (t>p->items.Count()) t=p->items.Count();
	if (t>0)
	{
		struct lvnode *n=(lvnode*)p->items.Find(t);
		int r=0;
		if (t>p->selected) r=1; else r=-1;
		while (n && n->locked)
		{
			t+=r;
			n=(lvnode*)p->items.Find(t);
		}
		if (!n) t=p->selected;
	}
	p->selected=t;
	if (t>0)
	{
		p->normal_selected=t;
		t=p->top;
		if ((p->selected-LINES)<p->top)
		{
			t=p->selected-LINES;
		}
		if ((p->selected+LINES)>(p->top+p->zeilen-1))
		{
			t=p->selected-p->zeilen+1+LINES;
		}
		if (t!=p->top) Top(t);
	}
	p->textaus(TA_SELECT);
}
int multilistview::Selected(void)
{
	if (p->selmode==MD_READONLY)
	{
		return Top();
	}
	return p->selmode==MD_EINFACH?p->normal_selected:p->selected;
}

int multilistview::SelectedColumns(void)
{
	return p->selectedcolumn+1;
}

void pmultilistview::draw(void)
{
	XDrawLine(o->display(),o->win,rubgc,rubpos,o->RealSize()+1,rubpos,o->height-o->RealSize()-1);
	XDrawLine(o->display(),o->win,rubgc,rubpos-1,o->RealSize()+1,rubpos-1,o->height-o->RealSize()-1);
}
void pmultilistview::setWidthOf(int col,int dw)
{
	specs[col].fixwidth=columnsize(o->width,col)+dw;
	o->Resize(o->width,o->height);

	char buf[100];
	sprintf(buf,"%d",specs[col].fixwidth);
	specs[col].innen.SetResource("width",buf);
}

void pmultilistview::setvars(void)
{ // Scroller setzen, Top korrigieren
	scroll.Visible(zeilen);
	scroll.Items(items.Count());
	o->Top(scroll.Position()+1);
}
void pmultilistview::checkvars(void)
{
	// selectedp stimmt, selected anpassen
	// top stimmt, topp anpassen

	if (items.IsEmpty())
	{
		topp=NULL;
		top=1;
		selected=0;
		selectedp=NULL;
		return;
	}
	if (topp)
	{
		if (!items.IsMember((node*)topp))
		{
			if (!(topp=(lvnode*)items.Find(top)))
			{
				topp=(lvnode*)items.tail;
			}
			if (topp && topp->base.next) top=items.Find((node*)topp); else top=0;
		}
		else
		{
			topp=(lvnode*)items.Find(top);
		}
	}
	else
	{
		topp=(lvnode*)items.Find(top);
	}
//	if (top)
//	{
//		if (top>items.Count()-zeilen+1) top=items.Count()-zeilen+1;
//		if (top<1) top=1;
//		topp=(lvnode*)items.Find(top);
//	}
	if (selectedp)
	{
		if (!items.IsMember((node*)selectedp))
		{
			selectedp=NULL;
			selected=0;
		}
		else
		{
			selected=items.Find((node*)selectedp);
		}
	}
	else
	{
		selected=0;
	}
}

void pmultilistview::textaus(int r,XEvent *event)
{ // r = TA_EXPOSE, r= TA_SCROLL, r= TA_SELECT, r= TA_EVENT
	if (!topp && r!=TA_EXPOSE) return;
	switch (r)
	{
		case TA_EVENT:
			if (event->type==GraphicsExpose)
			{
				// Zeilen zwischen event->xgraphicsexpose.y und 
				// event->xgraphicsexpose.height ausgeben
				struct lvnode *n=topp;
				int c=-1;

				for(int j=0;j<columns;j++)
				{
					if (event->xgraphicsexpose.drawable==specs[j].innen.gg_win()) c=j;
				}
				for(int i=0;i<zeilen;i++)
				{
					if (n->base.next)
					{
						if (i+1>=event->xgraphicsexpose.y/(fontheight+spacing)&&
						    i<=(event->xgraphicsexpose.y+event->xgraphicsexpose.height)/(fontheight+spacing))
						{
							zeileaus(n,c);
						}
						n=(lvnode*)n->base.next;
					}
					else
					{
						if (i>=event->xgraphicsexpose.y/(fontheight+spacing)&&
						    (i+1)<=(event->xgraphicsexpose.y+event->xgraphicsexpose.height)/(fontheight+spacing))
						{
							zeileclear(i,c);
						}
					}
				}
			}
			break;
		case TA_SCROLL:
			{
				int oldtop=items.Find((node*)topp);
				int y;
				topp=(lvnode*)items.Find(top);

				y=(oldtop-top)*(spacing+fontheight);
				for(int ii=0;ii<columns;ii++)
				{
					if (specs[ii].innen.gg_win())
					{
						XCopyArea(o->display(),specs[ii].innen.gg_win(),specs[ii].innen.gg_win(),specs[ii].innen.gg_gc(),0,-y,specs[ii].innen.gg_width(),zeilen*(fontheight+spacing),0,0);
						// oldtop > top:
						//               user nach oben
						//               area nach unten
						//               y>0
						// oldtop < top:
						//               user nach unten
						//               area nach oben
						//               y<0
						if (y<0)
						{ // area nach oben, unten füllen
							struct lvnode *n=topp;
							for(int i=0;i<zeilen;i++)
							{
								if (n->base.next)
								{
									if (i>=zeilen-(top-oldtop)) zeileaus(n,ii);
									n=(lvnode*)n->base.next;
								}
								else
								{
									if (i>=zeilen-(top-oldtop)) zeileclear(i,ii);
								}
							}
						}
						else if (y>0)
						{ // area nach unten, oben füllen
							struct lvnode *n=topp;
							for(int i=0;i<zeilen;i++)
							{
								if (n->base.next)
								{
									if (i<oldtop-top) zeileaus(n,ii);
									n=(lvnode*)n->base.next;
								}
								else
								{
									if (i<oldtop-top) zeileclear(i,ii);
								}
							}
						}
					}
				}
			}
			break;
		case TA_EXPOSE:
			{
				struct lvnode *n=topp;
				int c=-1;

				if (event)
				{
					for(int j=0;j<columns;j++)
					{
						if (event->xexpose.window==specs[j].innen.gg_win()) c=j;
					}
				}
				for(int i=0;i<zeilen;i++)
				{
					if (n && n->base.next)
					{
						zeileaus(n,c);
						n=(lvnode*)n->base.next;
					}
					else
					{
						zeileclear(i,c);
					}
				}
			}
			break;
		case TA_SELECT:
			{
				struct lvnode *n1=selectedp,*n2=(lvnode*)items.Find(selected);
				if (n1!=n2)
				{
					selectedp=NULL;
					zeileaus(n1);
					selectedp=n2;
					zeileaus(selectedp);
				}
				else
				{
					zeileaus(selectedp);
				}
			}
			break;
		case TA_SELNEW:
			if (selectedp)
			{
				zeileaus(selectedp);
			}			
			break;
		case TA_EXPOSELINE:
			if (event) zeileaus((lvnode*)event);
			break;
		case TA_EXPOSEBELOW:
			{
				struct lvnode *n=(lvnode*)event;
				if (items.Find((node*)n)<top)
					n=topp;

				for(int i=0;i<zeilen;i++)
				{
					if (n && n->base.next)
					{
						zeileaus(n);
						n=(lvnode*)n->base.next;
					}
				}
			}
			break;
	}
// 	if (o->locked)
// 	{
// 		for(int i=0;i<columns;i++)
// 		{
// 			if (specs[i].innen.gg_win())
// 			{
// 				glock.draw_locked(&specs[i].innen,0,0,specs[i].innen.gg_width(),specs[i].innen.gg_height());
// 			}
// 		}
// 	}
}
void pmultilistview::zeileclear(int z,int c)
{
	if (c==-1)
	{
		for (int i=0;i<columns;i++) zeileclear(z,i);
		return;
	}
	if (!specs[c].innen.gg_win() || z<0 || z>=zeilen) return;
	XSetForeground(o->display(),specs[c].innen.gg_gc(),bgcol(c));
	XFillRectangle(o->display(),specs[c].innen.gg_win(),specs[c].innen.gg_gc(),0,z*(fontheight+spacing),specs[c].innen.gg_width(),fontheight+spacing);
	if (o->locked)
	{
		for(int i=0;i<columns;i++)
		{
			if (specs[i].innen.gg_win())
			{
				glock.draw_locked(&specs[i].innen,0,z*(fontheight+spacing),specs[c].innen.gg_width(),fontheight+spacing);
			}
		}
	}
}
void pmultilistview::zeileaus(struct lvnode *n,int c)
{
	if (!n) return;
	if (c==-1)
	{
		for (int i=0;i<columns;i++) zeileaus(n,i);
		return;
	}
	if (!specs[c].innen.gg_win()) return;

	int z,y,tcol,hcol,p=0;

	z=items.Find((node*)n)-items.Find((node*)topp);
	if (z<0||z>=zeilen) return;

	y=z*(fontheight+spacing)+o->gg_font()->ascent;
	if (n==selectedp && n->marked && specs[c].cs.flags & CSF_SELECTABLE)
	{
		tcol=specs[c].innen.col_hitext();
		hcol=specs[c].innen.col_selected();
	}
	else if (n==selectedp && specs[c].cs.flags & CSF_SELECTABLE)
	{
		tcol=specs[c].innen.col_selectedtext();
		hcol=specs[c].innen.col_selected();
	}
	else if (n->locked)
	{
		tcol=specs[c].innen.col_shadow2();
		hcol=bgcol(c);
	}
	else if (n->marked)
	{
		tcol=specs[c].innen.col_text();
		hcol=specs[c].innen.col_markbackground();
	}
	else
	{
		tcol=specs[c].innen.col_text();
		hcol=bgcol(c);
	}

	XSetForeground(o->display(),specs[c].innen.gg_gc(),hcol);
	XFillRectangle(o->display(),specs[c].innen.gg_win(),specs[c].innen.gg_gc(),0,z*(fontheight+spacing),specs[c].innen.gg_width(),fontheight+spacing);
	XSetForeground(o->display(),specs[c].innen.gg_gc(),tcol);
	XSetBackground(o->display(),specs[c].innen.gg_gc(),hcol);
	if (specs[c].cs.flags & CSF_HOOK)
	{
		if (specs[c].cs.hookclass)
	 		fcb.set(specs[c].cs.hookclass,(CALLFUNCTION) specs[c].cs.objhook);
		else
	 		fcb.set((CBCAST) specs[c].cs.hook);

		hookdata.version=HOOKDATAVERSION;
		hookdata.number=items.Find((node*)n);
		hookdata.column=c+1;
		hookdata.x=0;
		hookdata.y=z*(fontheight+spacing);
		hookdata.width=specs[c].innen.gg_width();
		hookdata.height=fontheight;
		hookdata.flags=	(selectedp==n?HD_SELECTED:0)|
						(n->marked?HD_MARKED:0)|
						(n->locked?HD_LOCKED:0);
		hookdata.drawable=&specs[c].innen;
		hookdata.list=o;
		hookdata.itemdata=n->data;
		hookdata.userdata=specs[c].cs.userdata;
		p=(int)fcb.call((void*)&hookdata);
	}
	if (p<0)
	{
		XSetForeground(o->display(),specs[c].innen.gg_gc(),tcol);
		XSetBackground(o->display(),specs[c].innen.gg_gc(),hcol);
		p=-p;
	}

	int ln=strlen(n->text[c]);
	if (specs[c].cs.adjust!=ADJUST_LEFT || specs[c].cs.flags&CSF_AUTOSCALE)
	{
		XCharStruct cs;
		int dhint,fasc,fdec;
		XTextExtents(specs[c].innen.gg_font(),n->text[c],ln,&dhint,&fasc,&fdec,&cs);
		if (specs[c].cs.flags&CSF_AUTOSCALE && specs[c].fixwidth<=0)
		{
			if (specs[c].cs.flags&CSF_TITLE)
			{
				specs[c].title.GSetLimits();
				if (specs[c].title.gg_minwidth()>cs.width)
					cs.width=specs[c].title.gg_minwidth();
			}
			if (cs.width+(specs[c].cs.flags&CSF_SCALEABLE?SCALESIZE:0)+p>specs[c].minwidth)
			{
				specs[c].minwidth=cs.width+p+(specs[c].cs.flags&CSF_SCALEABLE?SCALESIZE:0);
				o->Resize(o->width,o->height);
			}
		}
		switch(specs[c].cs.adjust)
		{
			case ADJUST_RIGHT:
				p=specs[c].innen.gg_width()-p-cs.width;
				break;
			case ADJUST_CENTER:
				p=(specs[c].innen.gg_width()-p-cs.width)/2;
				break;
		}
	}
	XDrawImageString(o->display(),specs[c].innen.gg_win(),specs[c].innen.gg_gc(),p,y,n->text[c],ln);
	if (o->locked)
	{
		for(int i=0;i<columns;i++)
		{
			if (specs[i].innen.gg_win())
			{
				glock.draw_locked(&specs[i].innen,0,z*(fontheight+spacing),specs[c].innen.gg_width(),fontheight+spacing);
			}
		}
	}
}
void pmultilistview::title_callback(gadget *,int)
{
}

// -------------------------------------------------------------------

mlvtext::mlvtext(void) : gadget(), Ddraw(this), txt()
{
	NoKeyBorder();
	BorderSize(1);
	Font(FONT_GROUP);
	txt.ParentClass(this);
	txt.Name("mlvtext");
}
mlvtext::~mlvtext()
{
}
void mlvtext::Text(char *t)
{
	txt.NoUnderlineText(t);
	if (win) GExpose(NULL);
}
void mlvtext::GExpose(XEvent *event)
{
	XSetForeground(display(),gc,bgcol);
	XFillRectangle(display(),win,gc,0,0,width,height);
	borderUp(this,0,0,width,height);
	txt.draw_normal(this,RealSize()+SCALESIZE,RealSize()+2,width-SCALESIZE,height-RealSize()*2);
}
int mlvtext::GActivate(XEvent *event,int why)
{
	return 0;
}
int mlvtext::GSelected(XEvent *event,int why,int wasabove)
{
	return 0;
}
void mlvtext::GSetLimits(void)
{
	minwidth=maxwidth=txt.width(this)+RealSize()*2+SCALESIZE;
	minheight=maxheight=txt.height(this)+RealSize()*2+4;
}
int mlvtext::Create(void)
{
	Background(bgcol); //col_background());
	if (gadget::Create())
	{
		Mode(GM_ONEEXPOSE);
		return 1;
	}
	return 0;
}

#endif
