/*

Directory Opus 4
Original GPL release version 4.12
Copyright 1993-2000 Jonathan Potter

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

All users of Directory Opus 4 (including versions distributed
under the GPL) are entitled to upgrade to the latest version of
Directory Opus version 5 at a reduced price. Please see
http://www.gpsoft.com.au for more information.

The release of Directory Opus 4 under the GPL in NO WAY affects
the existing commercial status of Directory Opus 5.

*/

#include "dopus.h"

/* Allocate or free directory buffers as necessary to achieve desired
   buffer count in each window */

void allocdirbuffers(newnum)
int newnum;
{
	int win,a,num;
	struct DirectoryWindow *dir,*next;

	for (win=0;win<2;win++) {
		if (data_buffer_count[win]>newnum) {
			dir=dopus_firstwin[win];
			for (a=0;a<newnum;a++) dir=dir->next;
			dir->last->next=dopus_firstwin[win];
			dopus_firstwin[win]->last=dir->last;
			num=data_buffer_count[win];
			for (a=newnum;a<num;a++) {
				next=dir->next;
				freedir(dir,-1);
				if (dopus_curwin[win]==dir) dopus_curwin[win]=dopus_firstwin[win];
				FreeMem(dir,sizeof(struct DirectoryWindow));
				if ((--data_buffer_count[win])<1) break;
				dir=next;
			}
		}
		else if (data_buffer_count[win]<newnum) {
			dir=dopus_firstwin[win];
			for (a=0;a<data_buffer_count[win]-1;a++) dir=dir->next;
			for (a=data_buffer_count[win];a<newnum;a++) {
				if (next=AllocMem(sizeof(struct DirectoryWindow),MEMF_CLEAR)) {
					if (!dir) {
						dopus_firstwin[win]=next;
						dopus_curwin[win]=next;
						dir=next;
					}
					dir->next=next;
					next->last=dir;
					next->next=dopus_firstwin[win];
					dopus_firstwin[win]->last=next;
					next->number=a;
					next->hlen=145;
					++data_buffer_count[win];
					dir=next;
				}
			}
		}
	}
}

/* Searches both buffer lists for a given directory, and displays it in
   the active window if found (copying it over if necessary) */

bringinbuffer(dirbuf,win,read)
char *dirbuf;
int win,read;
{
	int a,otherwin;
	struct DirectoryWindow *dir;

	if (status_iconified || !dirbuf) return(0);
	checkcurrentbuffer(win);
	if (!(findbuffer(dirbuf,win,0,0))) {
		otherwin=1-data_active_window;
		dir=dopus_firstwin[otherwin];
		for (a=0;a<data_buffer_count[otherwin];a++,dir=dir->next) {
			if ((LStrCmpI(dirbuf,dir->directory))==0) {
				incrementbuf(data_active_window,1,0);
				copydirwin(dir,dopus_curwin[data_active_window],data_active_window);
				check_old_buffer(data_active_window);
				return(1);
			}
		}
		if (read) {
			strcpy(str_pathbuffer[data_active_window],dirbuf);
			startgetdir(data_active_window,SGDFLAGS_CANMOVEEMPTY);
		}
		else return(0);
	}
	return(1);
}

/* Searches forward from the current buffer to find an empty one, or one
   with the same name (it looks first for one with the same name).
   If a buffer is not found, it uses the current buffer */

void findemptybuffer(win)
int win;
{
	struct DirectoryWindow *dir;
	int a,ok=0;
	char fullbuf[256];

	expand_path(str_pathbuffer[win],fullbuf);
	checkdir(str_pathbuffer[win],NULL);
	checkdir(fullbuf,NULL);

	dir=dopus_curwin[win];
	for (a=0;a<data_buffer_count[win];a++) {
		if (LStrCmpI(dir->directory,str_pathbuffer[win])==0 ||
			LStrCmpI(dir->directory,fullbuf)==0) {
			ok=1;
			break;
		}
		dir=dir->next;
	}
	if (!ok) {
		dir=dopus_curwin[win];
		for (a=0;a<data_buffer_count[win];a++) {
			if (!dir->directory[0]) break;
			dir=dir->next;
		}
	}
	dopus_curwin[win]=dir;
}

/* Sets up and reads a new directory */

void startgetdir(win,flags)
int win,flags;
{
	checkcurrentbuffer(win);

	if (!(config->dirflags&DIRFLAGS_CHECKBUFS && (flags&SGDFLAGS_CANCHECKBUFS)) ||
		!(findbuffer(str_pathbuffer[win],win,1,0))) {
		if ((config->dirflags&DIRFLAGS_FINDEMPTY) && (flags&SGDFLAGS_CANMOVEEMPTY))
			findemptybuffer(win);
		if (str_pathbuffer[win][0]==0) expand_path("",str_pathbuffer[win]);
		checkdir(str_pathbuffer[win],&path_strgadget[win]);
		strcpy(dopus_curwin[win]->directory,str_pathbuffer[win]);
		getdir(dopus_curwin[win],win,(flags&SGDFLAGS_REREADINGOLD));
	}
	seename(win);
	refreshwindow(win,0);
}

/* Moves forward or backward a buffer and refreshes display */

void incrementbuf(win,dir,show)
int win,dir,show;
{
	advancebuf(win,dir);
	strcpy(str_pathbuffer[win],dopus_curwin[win]->directory);
	checkdir(str_pathbuffer[win],&path_strgadget[win]);
	checksize(win);
	check_old_buffer(win);
	if (show) {
		seename(win);
		refreshwindow(win,1);
		doselinfo(win);
	}
	startnotify(win);
}

/* Copies the contents of one window to another */

void copydirwin(sourcewin,destwin,dest)
struct DirectoryWindow *sourcewin,*destwin;
int dest;
{
	struct Directory *copy;

	if (status_iconified) return;
	busy();
	freedir(destwin,dest);
	copy=sourcewin->firstentry;
	strcpy(destwin->realdevice,sourcewin->realdevice);
	while (copy) {
		if (!(addfile(destwin,dest,copy->name,copy->size,copy->type,&copy->date,
			copy->comment,copy->protection,copy->subtype,FALSE,copy->dispstr,NULL,
			copy->owner_id,copy->group_id))) break;
		copy=copy->next;
	}
	strcpy(str_pathbuffer[dest],sourcewin->directory);
	strcpy(destwin->directory,sourcewin->directory);
	strcpy(destwin->diskname,sourcewin->diskname);
	destwin->hlen=sourcewin->hlen;
	destwin->flags=sourcewin->flags;
	copy_datestamp(&sourcewin->dirstamp,&destwin->dirstamp);
	refreshwindow(dest,1);
	checkdir(str_pathbuffer[dest],&path_strgadget[dest]);
	checksize(dest);
	last_selected_entry=NULL;
	okay();
	unbusy();
}

/* Swaps the contents of the two windows */

void swapdirwin()
{
	int tmp;
	struct DirectoryWindow tempwin;

	if (status_iconified) return;

	CopyMem((char *)dopus_curwin[0],(char *)&tempwin,sizeof(struct DirectoryWindow));
	tempwin.next=dopus_curwin[1]->next; tempwin.number=dopus_curwin[1]->number;
	tempwin.last=dopus_curwin[1]->last;
	dopus_curwin[1]->next=dopus_curwin[0]->next; dopus_curwin[1]->number=dopus_curwin[0]->number;
	dopus_curwin[1]->last=dopus_curwin[0]->last;
	CopyMem((char *)dopus_curwin[1],(char *)dopus_curwin[0],sizeof(struct DirectoryWindow));
	CopyMem((char *)&tempwin,(char *)dopus_curwin[1],sizeof(struct DirectoryWindow));

	for (tmp=0;tmp<2;tmp++) {
		strcpy(str_pathbuffer[tmp],dopus_curwin[tmp]->directory);
		refreshwindow(tmp,1);
		checkdir(str_pathbuffer[tmp],&path_strgadget[tmp]);
		checksize(tmp);
	}
	okay();
}

/* Moves forwards or backwards a directory */

void advancebuf(win,dir)
int win,dir;
{
	int a;

	endnotify(win);
	if (!checkcurrentbuffer(win)) {
		if (dir>0) {
			for (a=0;a<dir;a++) dopus_curwin[win]=dopus_curwin[win]->next;
		}
		else {
			for (a=dir;a<0;a++) dopus_curwin[win]=dopus_curwin[win]->last;
		}
	}
}

/* Clears the contents of all unshown buffers */

void clearbuffers()
{
	int a,win;
	struct DirectoryWindow *dir;

	busy();
	for (win=0;win<2;win++) {
		dir=dopus_firstwin[win];
		for (a=0;a<data_buffer_count[win];a++) {
			if (dopus_curwin[win]!=dir) {
				freedir(dir,-1);
				dir->directory[0]=0;
			}
			dir=dir->next;
		}
	}
	unbusy();
	dostatustext(globstring[STR_BUFFERS_CLEARED]);
}

/* Searches backwards for a named buffer and moves to it if found */

struct DirectoryWindow *findbuffer(dirbuf,win,canchecklocks,onlyreturn)
char *dirbuf;
int win,canchecklocks,onlyreturn;
{
	int a,founddir=0,ret=0,try,checklocks=0;
	struct DirectoryWindow *dir;
	BPTR lock=0,testlock;
	struct FileInfoBlock __aligned fblock;
	char tempbuf[300];

	if (status_iconified) return(NULL);
	dir=dopus_curwin[win];
	main_proc->pr_WindowPtr=(APTR)-1;

	strcpy(tempbuf,dirbuf);

	if (config->dirflags&DIRFLAGS_EXPANDPATHS) {
		canchecklocks=0;
		expand_path(dirbuf,tempbuf);
	}

	TackOn(tempbuf,NULL,300);

	for (try=0;try<2;try++) {
		if (checklocks && !(lock=Lock(tempbuf,ACCESS_READ))) break;

		for (a=0;a<data_buffer_count[win];a++,dir=dir->last) {
			if (dir->directory[0]) {
				if (checklocks &&
					(testlock=Lock(dir->directory,ACCESS_READ))) {
					if (CompareLock(lock,testlock)==LOCK_SAME) founddir=1;
					UnLock(testlock);
					if (!founddir) continue;
				}
				if (founddir || (LStrCmpI(tempbuf,dir->directory))==0) {
					if (!(lockandexamine(tempbuf,&fblock)) ||
						(CompareDate(&fblock.fib_Date,&dir->dirstamp)!=0)) continue;
					if (!onlyreturn) go_to_buffer(win,dir);
					ret=1;
					break;
				}
			}
		}
		if (ret) break;
		if (canchecklocks) checklocks=1;
		else break;
	}
	if (lock) UnLock(lock);
	if (config->errorflags&ERROR_ENABLE_DOS) main_proc->pr_WindowPtr=(APTR)Window;
	return((ret)?dir:NULL);
}

/* Checks all buffers for a pathname and changes it to the new
   pathname if found */

void renamebuffers(old,new)
char *old,*new;
{
	int a,win;
	struct DirectoryWindow *dir;

	for (win=0;win<2;win++) {
		dir=dopus_firstwin[win];
		for (a=0;a<data_buffer_count[win];a++) {
			if (replacepart(dir->directory,old,new) && dopus_curwin[win]==dir) {
				strcpy(str_pathbuffer[win],dir->directory);
				checkdir(str_pathbuffer[win],&path_strgadget[win]);
			}
			dir=dir->next;
		}
	}
}

replacepart(string,old,new)
char *string,*old,*new;
{
	int oldlen,stringlen;
	char tempbuf[256];

	oldlen=strlen(old);
	if (LStrnCmpI(string,old,oldlen)) return(0);
	if ((stringlen=strlen(string))>oldlen) {
		stringlen-=oldlen;
		strncpy(tempbuf,&string[oldlen],stringlen);
		tempbuf[stringlen]=0;
	}
	else stringlen=0;
	strcpy(string,new);
	if (stringlen>0) StrConcat(string,tempbuf,256);
	return(1);
}

/* Generates a list of all available buffers in a window */

void dolistbuffers(destwin)
int destwin;
{
	int a,b,c,win;
	struct DateStamp ds;
	struct DirectoryWindow *dir;
	char name[2];

	if (status_iconified) return;
	makespecialdir(destwin,globstring[STR_BUFFER_LIST_TITLE]);

	busy(); b=scrdata_dispwin_nchars[destwin]; name[0]=0;
	DateStamp(&ds);
	for (win=0;win<2;win++) {
		dir=dopus_firstwin[win];
		for (a=0;a<data_buffer_count[win];a++) {
			if (dir->directory[0]) {
				addfile(dopus_curwin[destwin],destwin,name,0,
					ENTRY_CUSTOM,&ds,dir->directory,0,
					CUSTOMENTRY_BUFFERLIST,FALSE,dir->directory,NULL,0,0);
				if ((c=strlen(dir->directory))>b) b=c;
			}
			dir=dir->next;
		}
	}
	dopus_curwin[destwin]->hlen=b;
	refreshwindow(destwin,3);
	unbusy();
	okay();
}

/* Check if buffer is a special buffer, and return to normal if so */

checkcurrentbuffer(win)
int win;
{
	if (dopus_curwin[win]==dopus_specialwin[win]) {
		dopus_curwin[win]=dopus_oldwin[win];
		return(1);
	}
	return(0);
}

/* Send message to custom entry handler */

void userentrymessage(dir,entry,type)
struct DirectoryWindow *dir;
struct Directory *entry;
int type;
{
	struct RexxMsg *msg,*reply;
	struct MsgPort *port;
	int fail=0;

	/* If no customhandler installed or rexx library is not there, return */
	if (!dir->custhandler[0] || !RexxSysBase) return;

	/* Attempt to create the message packet */
	if (!(msg=CreateRexxMsg(general_port,NULL,str_arexx_portname))) fail=1;
	else {
		/* Set message type (and argument count of 4) */
		msg->rm_Action=RXFUNC|4;

		/* Fill out argument pointers.

		   Arg0   = operation type
		   Arg1   = entry number
		   Arg2   = entry text
		   Arg3   = userdata          */

		msg->rm_Args[0]=(STRPTR)type;
		msg->rm_Args[1]=entry->name;
		msg->rm_Args[2]=entry->comment;
		msg->rm_Args[3]=(STRPTR)entry->protection;

		/* Fill in the message correctly.
		   Arguments are integer/string/string/integer
                        1       0      0      1       = 9 */

		if (!(FillRexxMsg(msg,4,9))) fail=1;
	}

	/* If we failed to create the message properly, release it and return */
	if (fail) {
		if (msg) DeleteRexxMsg(msg);
		doerror(103);
		return;
	}

	/* Forbid() to find the port */
	Forbid();
	if (!(port=FindPort(dir->custhandler))) {
		char message[200];

		/* Port was not out there; put up an error requester */
		lsprintf(message,globstring[STR_CUSTPORT_NOT_FOUND],dir->custhandler);
		simplerequest(message,str_okaystring,NULL);

		/* Clear message and return */
		ClearRexxMsg(msg,4);
		DeleteRexxMsg(msg);
		Permit();
		return;
	}

	/* Turn busy pointer on and send the message */
	busy();
	PutMsg(port,(struct Message *)msg);

	Permit();
	SetSignal(0,INPUTSIG_ABORT);

	FOREVER {
		/* If abort sequence hit, break out immediately. The message is now
		   lost to us, we can never free it */
		if ((Wait(1<<general_port->mp_SigBit|INPUTSIG_ABORT))&INPUTSIG_ABORT) {
			status_haveaborted=status_justabort=0;
			break;
		}

		/* If message has been replied, free it and break out */
		if (reply=(struct RexxMsg *)GetMsg(general_port)) {
			ClearRexxMsg(msg,4);
			DeleteRexxMsg(msg);
			break;
		}
	}
	unbusy();
	okay();
}

/* Opens the "special" dir in the window for a custom list */

void makespecialdir(win,title)
int win;
char *title;
{
	freedir(dopus_specialwin[win],win);
	if (dopus_curwin[win]!=dopus_specialwin[win]) {
		dopus_oldwin[win]=dopus_curwin[win];
		dopus_curwin[win]=dopus_specialwin[win];
	}
	strcpy(dopus_curwin[win]->diskname,title);
	str_pathbuffer[win][0]=0;
	checkdir(str_pathbuffer[win],&path_strgadget[win]);
	freedir(dopus_curwin[win],win);
	dopus_curwin[win]->diskfree=dopus_curwin[win]->disktot=dopus_curwin[win]->diskblock=-1;
}

/* Checks buffers to see if they need re-reading */

void check_old_buffer(win)
int win;
{
	int reread=0;

	if (config->dirflags&DIRFLAGS_REREADOLD && dopus_curwin[win]->directory[0]) {
		if (dopus_curwin[win]->firstentry &&
			(dopus_curwin[win]->firstentry->type==ENTRY_CUSTOM ||
			dopus_curwin[win]->firstentry->type==ENTRY_DEVICE)) return;
		else {
			struct FileInfoBlock __aligned testinfo;

			main_proc->pr_WindowPtr=(APTR)-1;
			if (lockandexamine(dopus_curwin[win]->directory,&testinfo)) {
				if (CompareDate(&dopus_curwin[win]->dirstamp,&testinfo.fib_Date)<0)
					reread=1;
				else if (!(config->dirflags&DIRFLAGS_EXPANDPATHS) &&
					dopus_curwin[win]->volumename[0]) {

					char rootname[256];

					strcpy(rootname,dopus_curwin[win]->directory);
					if (getroot(rootname,NULL) &&
						(strcmp(rootname,dopus_curwin[win]->volumename))!=0)
						reread=1;
				}
			}
			if (config->errorflags&ERROR_ENABLE_DOS) main_proc->pr_WindowPtr=(APTR)Window;
		}
		if (reread) {
			struct DirWindowPars notifypars;

			notifypars.reselection_list=NULL;
			makereselect(&notifypars,win);
			startgetdir(win,SGDFLAGS_REREADINGOLD);
			doreselect(&notifypars,0);
			makereselect(&notifypars,-1);
		}
	}
}

/* Refresh the display of a window */

void refreshwindow(win,type)
int win,type;
{
	if (win>-1) {
		if (dopus_curwin[win]->offset>dopus_curwin[win]->total-scrdata_dispwin_lines)
			dopus_curwin[win]->offset=dopus_curwin[win]->total-scrdata_dispwin_lines;
		if (dopus_curwin[win]->offset<0)
			dopus_curwin[win]->offset=0;

		if (type&1) {
			fixprop(win);
			fixhorizprop(win);
		}
		if (type&2) displayname(win,1);
		dopus_curwin[win]->oldoff=dopus_curwin[win]->oldhoff=-1;
		displaydir(win);
	}
}

/* Move to a buffer */

void go_to_buffer(win,dir)
int win;
struct DirectoryWindow *dir;
{
	endnotify(win);
	dopus_curwin[win]=dir;
	strcpy(str_pathbuffer[win],dir->directory);
	checkdir(str_pathbuffer[win],&path_strgadget[win]);
	checksize(win);
	check_old_buffer(win);
	refreshwindow(win,1);
	doselinfo(win);
	startnotify(win);
}
