#ifndef CODE_DIRVIEW_CXX
#define CODE_DIRVIEW_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: dirview.cxx,v 1.5 1999/04/18 18:16:57 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 <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <Xclasses/gadget.h>
#include <Xclasses/dirview.h>
#include "include/dirview_p.h"
#include <Xclasses/gfx_file.h>
#include <Xclasses/gfx_dir.h>
#include <Xclasses/gfx_command.h>

static gfx_file 	dir_file;
static gfx_dir		dir_dir;
static gfx_command	dir_command;

int dirhook(struct hook_data *h)
{
	if (h->column!=1) return 0;
	struct filenode *n;
	n=(filenode*)h->itemdata;
	if (S_ISDIR(n->file.st_mode))
	{
		if (h->flags&HD_SELECTED)
		{
			dir_dir.draw_selected(h->drawable,h->x,h->y+1,h->height,h->drawable->gg_font()->ascent);
		}
		else
		{
			dir_dir.draw_normal(h->drawable,h->x,h->y+1,h->height,h->drawable->gg_font()->ascent);
		}
	}
	else if (n->file.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
	{
		if (h->flags&HD_SELECTED)
		{
			dir_command.draw_selected(h->drawable,h->x,h->y+1,h->height,h->drawable->gg_font()->ascent);
		}
		else
		{
			dir_command.draw_normal(h->drawable,h->x,h->y+1,h->height,h->drawable->gg_font()->ascent);
		}
	}
	else
	{
		if (h->flags&HD_SELECTED)
		{
			dir_file.draw_selected(h->drawable,h->x,h->y+1,h->height,h->drawable->gg_font()->ascent);
		}
		else
		{
			dir_file.draw_normal(h->drawable,h->x,h->y+1,h->height,h->drawable->gg_font()->ascent);
		}
	}
	if (S_ISLNK(n->lfile.st_mode))
	{
		XSetForeground(h->list->display(),h->drawable->gg_gc(),h->list->col_shadow());
		XFillRectangle(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
					h->x+3,h->y+h->height-7,7,7);
		XSetForeground(h->list->display(),h->drawable->gg_gc(),h->list->col_white());
		XFillRectangle(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
					h->x+4,h->y+h->height-6,5,5);

 		XSetForeground(h->list->display(),h->drawable->gg_gc(),h->list->col_black());
 		XDrawLine(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
 					h->x+5,h->y+h->height-5,
 					h->x+5,h->y+h->height-3);
 		XDrawLine(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
 					h->x+5,h->y+h->height-3,
 					h->x+7,h->y+h->height-3);

// 		XSetForeground(h->list->display(),h->drawable->gg_gc(),h->list->col_black());
// 		XFillRectangle(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
// 					h->x+3,h->y+h->height-6,3,6);
// 		XFillRectangle(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
// 					h->x+3,h->y+h->height-3,5,3);

// 		XSetForeground(h->list->display(),h->drawable->gg_gc(),h->list->col_white());
//  		XDrawLine(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
//  					h->x+4,h->y+h->height-5,
//  					h->x+4,h->y+h->height-2);
//  		XDrawLine(h->list->display(),h->drawable->gg_win(),h->drawable->gg_gc(),
//  					h->x+4,h->y+h->height-2,
//  					h->x+6,h->y+h->height-2);
	}

// 	if (S_ISDIR(n->file.st_mode))
// 	{
// 		XSetForeground(h->list->display(),h->drawable->gg_gc(),h->list->col_shadow());
// 	}
// 	else
// 	{
// 		XSetForeground(h->list->display(),h->drawable->gg_gc(),h->list->col_text());
// 	}
//	return (h->height+2);
	return -(h->height+2);
}

dirview::dirview() : multilistview()
{
	while (!(p=new pdirview))
	{
		outOfMemory("dirview");
	}
	p->o=this;
	Name("dirview");

	column_spec specs[2];
	specs[0].flags=CSF_USEWEIGHT|CSF_HOOK|CSF_SELECTABLE|CSF_USESHORTCUT|CSF_TITLE|CSF_AUTOSCALE;
	specs[0].weight=80;
//	specs[0].flags=CSF_USEWEIGHT|CSF_HOOK|CSF_SELECTABLE|CSF_USESHORTCUT|CSF_TITLE;
//	specs[0].weight=100;
	specs[0].hookclass=NULL;
	specs[0].hook=dirhook;
	specs[0].title="Filename";
	specs[0].userdata=NULL;
	specs[1].flags=CSF_USEWEIGHT|CSF_SELECTABLE|CSF_ADJUST|CSF_TITLE;
	specs[1].weight=100;
//	specs[1].flags=CSF_USEWIDTH|CSF_SELECTABLE|CSF_ADJUST|CSF_TITLE;
//	specs[1].width=60;
	specs[1].adjust=ADJUST_RIGHT;
	specs[1].title="Size";
	Columns(2,specs);
}
dirview::dirview(char *dir) : multilistview()
{
	while (!(p=new pdirview))
	{
		outOfMemory("dirview");
	}
	p->o=this;
	strncpy(p->curdir,dir,sizeof(p->curdir));
	Name("dirview");

	column_spec specs[2];
	specs[0].flags=CSF_USEWEIGHT|CSF_HOOK|CSF_SELECTABLE|CSF_USESHORTCUT;
	specs[0].weight=100;
	specs[0].hookclass=NULL;
	specs[0].hook=dirhook;
	specs[0].userdata=NULL;
	specs[1].flags=CSF_USEWIDTH|CSF_SELECTABLE|CSF_ADJUST;
	specs[1].width=50;
	specs[1].adjust=ADJUST_RIGHT;
	Columns(2,specs);
}

pdirview::pdirview() : files()
{
	strcpy(curdir,"");
	getcwd(curdir,sizeof(curdir));
	strcpy(pattern,"*");
	dirsfirst=0;
	showperiod=0;
	showdirs=1;
	showfiles=1;
	dirmode=0;
	dclick=0;
	dirip=NULL;
	dfd=NULL;
	dptr=NULL;
}
pdirview::~pdirview()
{
}
dirview::~dirview()
{
	if (p->dfd)
	{
		closedir(p->dfd);
		p->dfd=NULL;
	}
	p->RemoveAll();
	if (p) delete p;
}
int dirview::Create(void)
{ 
	ShowSelected();
    if (multilistview::Create())
    {
		Mode(gg_mode()|GM_NORMALTICK|GM_KEYTICK);
    	SetDir(p->curdir);
        return 1;
    } 
    return 0;
}
void dirview::Free(void)
{
	multilistview::Free();
}
void dirview::ShowPeriod(void)
{
	p->showperiod=1;
}
void dirview::NoPeriod(void)
{
	p->showperiod=0;
}
void dirview::DirsFirst(void)
{
	p->dirsfirst=1;
}
void dirview::DirsNormal(void)
{
	p->dirsfirst=0;
}
void dirview::DirsOnly(void)
{
	p->showdirs=1;
	p->showfiles=0;
}
void dirview::FilesOnly(void)
{
	p->showdirs=0;
	p->showfiles=1;
}
void dirview::DirsFiles(void)
{
	p->showdirs=1;
	p->showfiles=1;
}
void dirview::DirSelector(void)
{
	DirsOnly();
	p->dirmode=1;
}
void dirview::FileSelector(void)
{
	DirsFiles();
	p->dirmode=0;
}

int dirview::FindNextMarked(int old,char *buf,int size)
{
	int i,r;
	do
	{
		r=0;
		if (i=multilistview::FindNextMarked(old))
		{
			struct filenode *n;
			buf[0]=0;
			if (n=(filenode*)GetData(i))
			{
				if (!(n->file.st_mode&S_IFDIR))
				{
					strncpy(buf,n->name,size-1);
				}
				else
				{
					r=1;
					old=i;
				}
			}
		}
	}
	while (r);
	return i;
}
int dirview::IsSelectedMarked(void)
{
	return IsMarked(Selected());
}

void dirview::SetPattern(char *d)
{
	strncpy(p->pattern,d,sizeof(p->pattern));
	RereadDir();
}
void dirview::ApplyInput(input &ip)
{
	ApplyInput(&ip);
}
void dirview::ApplyInput(input *ip)
{
	p->dirip=ip;
}
void dirview::RereadDir(void)
{
	SetDir(p->curdir);
}
int dirview::SetDir(char *d)
{
	int r=0;
	if (p->dfd)
	{
		closedir(p->dfd);
		p->dfd=NULL;
	}

	strncpy(p->curdir,d,sizeof(p->curdir));
	p->RemoveAll();

	if (p->curdir[0]=='~')
	{ // home of user
		char *buf,*d2=d;
		if (p->curdir[1]=='/' || p->curdir[1]==0)
		{ // my home
			if (buf=getenv("HOME"))
			{
				d2=d+1;
			}
			else
				buf="";
		}
		else
		{ // other home
			char uname[20],*c=d;
			int i=0;
			struct passwd *pw;

			uname[0]=0; c++;
			while (i<20 && *c && *c!='/')
			{
				uname[i]=*c;
				i++; c++;
			}
			uname[i]=0;
			
			if (pw=getpwnam(uname))
			{
				buf=pw->pw_dir;
				d2=d+1+i;
			}
			else
				buf="";
		}
		strncpy(p->curdir,buf,sizeof(p->curdir));
		strncat(p->curdir,d2,sizeof(p->curdir));
	}
	{
		char *c=p->curdir,*lsl1=c,*lsl2=c;
		while (*c)
		{
			if (*c=='/' && *(c+1)=='/')
			{
				strcpy(c,c+1); c--;
			}
			else if (*c=='/' && *(c+1)=='.' && (*(c+2)=='/' || *(c+2)==0))
			{
				strcpy(c,c+2);
				c-=2;
			}
			else
			{
				if (*c=='/') { lsl1=lsl2; lsl2=c; }
			}
			if (*c=='.' && *(c+1)=='.' && (*(c+2)=='/' || *(c+2)==0))
			{
				strcpy(lsl1,c+2);
				c=lsl1-1;
			}
			c++;
		}
	}
	if (p->curdir[strlen(p->curdir)-1]!='/') strcat(p->curdir,"/");

	if (p->dirip)
	{
		p->dirip->Default(p->curdir);
	}

	if (strcmp(p->curdir,"/")) p->addfile("..");

	if (p->dfd)
	{
		closedir(p->dfd);
		p->dfd=NULL;
	}
	if (p->dfd=opendir(p->curdir))
	{
// 		while (p->dptr=readdir(p->dfd))
// 		{
// 			if (strcmp(p->dptr->d_name,".."))
// 			{
// 				p->addfile(p->dptr->d_name);
// 			}
// 		}
// 		closedir(p->dfd);
//		p->dfd=NULL;
	}
	else
		r=1;
	return r;
}
void dirview::GetDir(char *d,int size)
{
	strncpy(d,p->curdir,size-1);
}
void dirview::GetFile(char *d,int size)
{
	int s=Selected();
	struct filenode *n;
	
	d[0]=0;
	if (s)
	{
		if (n=(filenode*)GetData(s))
		{
			if (!(n->file.st_mode&S_IFDIR))
			{
				strncpy(d,n->name,size-1);
			}
		}
	}
}

void dirview::GExpose(XEvent *event)
{
	multilistview::GExpose(event);
}
int dirview::GActivate(XEvent *event,int why)
{
	if (why==GA_TICK)
	{
		p->readdirtick();
		if (!gg_active() || gg_active()==GA_KEY) return 0;
	}
	return multilistview::GActivate(event,why);
}
int dirview::GSelected(XEvent *event,int why,int wasabove)
{
	if (multilistview::GSelected(event,why,wasabove))
	{
		int s;
		struct filenode *n;

		s=Selected();
		n=(filenode*)GetData(s);
		if (n->file.st_mode&S_IFDIR)
		{
			if (active&GA_DCLICK)
			{
				p->addpath(n->name);
				SetDir(p->curdir);
			}
			return GSR_MESSAGE|(p->dirmode?GSR_SELECTION:0);
		}
		else
		{
			return GSR_SELECTION|GSR_MESSAGE;
		}
	}
	return 0;
}
void pdirview::RemoveAll(void)
{
	o->RemoveAll();
	struct filenode *n1;
	
	while (n1=(filenode*)files.RemHead())
	{
		delete [] (char*)n1;
	}
}
#define TICKDELAY	50000U
void pdirview::readdirtick(void)
{
	if (dfd)
	{
		struct timeval t1,t2;
		gettimeofday(&t2,NULL);

		while (dptr=readdir(dfd))
		{
			if (strcmp(dptr->d_name,".."))
			{
				addfile(dptr->d_name);
			}

			gettimeofday(&t1,NULL);
			if (((t1.tv_sec-t2.tv_sec)*1000000+(t1.tv_usec-t2.tv_usec))>=TICKDELAY)
			{
				return;
			}
		}
		closedir(dfd);
		dfd=NULL;
	}
}
void pdirview::addfile(char *fnme)
{
	struct filenode *n1,*n2;
	int m,fertig=0;
	char *oldfnme=fnme;
	while (*fnme)
	{
		if (*fnme=='/') oldfnme=fnme+1;
		fnme++;
	}
	fnme=oldfnme;
	if (!strcmp(fnme,".")) return;
	if (strcmp(fnme,"..") && !showperiod && fnme[0]=='.' && !glob_string(fnme,pattern,!showperiod)) return;
	if (n1=(filenode *)new char[sizeof(filenode)+strlen(fnme)+1])
	{
		strcpy(n1->name,fnme);
		strcpy(buf,curdir);
		strcat(buf,fnme);
		stat(buf,&n1->file);
		lstat(buf,&n1->lfile);
		if ((S_ISDIR(n1->file.st_mode)&&showdirs)||(S_ISREG(n1->file.st_mode)&&showfiles))
		{
			if (S_ISDIR(n1->file.st_mode) || (S_ISREG(n1->file.st_mode) && glob_string(fnme,pattern,!showperiod)))
			{
				if (dirsfirst && (n1->file.st_mode&S_IFDIR)) m=0; else m=1;
				n2=(filenode*)files.head;
				while (n2->base.next && !fertig)
				{
					if (m==0 && !(n2->file.st_mode&S_IFDIR))
					{
						fertig=1;
						files.Insert((node*)n1,(node*)n2);
					}
					else if (strcasecmp(fnme,n2->name)<0)
					{
						if (!dirsfirst || (n2->file.st_mode&S_IFDIR)==(n1->file.st_mode&S_IFDIR))
						{
							fertig=1;
							files.Insert((node*)n1,(node*)n2);
						}
					}
					n2=(filenode*)n2->base.next;
				}
				if (!fertig) files.AddTail((node*)n1);
				strcpy(buf,fnme);
				m=files.Find((node*)n1);

				if (S_ISREG(n1->file.st_mode))
					sprintf(buf2,"%d",n1->file.st_size);
				else
					buf2[0]=0;
				barr[0]=buf;
				barr[1]=buf2;
				o->AddBefore(m,barr,n1);
			}
			else
			{
				delete n1;
			}
		}
		else
		{
			delete n1;
		}
	}
}
void pdirview::addpath(char *dir)
{
	if (strcmp(dir,"..")==0)
	{
		for(int i=strlen(curdir)-2;i>=0;i--)
		{
			if (curdir[i]=='/')
			{
				curdir[i]=0;
				i=0;
			}
		}
	}
	else
	{
		strcat(curdir,dir);
	}
}

#endif
