/*
 *	Mouse.c - Copyright © 1991 by S.R. & P.C.
 *
 *	Created:	21 Feb 1991  09:41:20
 *	Modified:	26 Jul 1991  18:14:48
 *
 *	Make>> make
 */

#include "Global.h"
#include "Actions.h"
#include "FileList.h"
#include "proto/Mouse.h"
#include "proto/Windows.h"
#include "proto/Scan.h"
#include "proto/Run.h"
#include "proto/Process.h"
#include "proto/Request.h"
#include "proto/Draw.h"
#include "proto/File.h"
#include "proto/FileList.h"


extern struct BrowserWindow *CurrentWin;
extern struct Screen *Screen;
extern struct Config Config;
extern struct MinList WindowList;
extern char *ReqTitle;
extern short SelectNum;
extern long SelectBytes, SelectBlocks;
extern BPTR BrowserDir;

static struct ScrollEntry *LastSelected;
static BOOL SelectDown;		/* Tell if left mouse button is down and if last 
							 * selectdown event made an entry being selected */

static short PointerMatrix[36] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3168, 3168,
	2080, 3168, 0, 640, 256, 0, 0, 640, 2080, 3168, 3168, 3168,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

#define CROSS_XOFFSET	-8L
#define CROSS_YOFFSET	-7L
#define CROSS_SIZE		72L


/* Open window and scan directory referenced by BPTR DirLock */

static void OpenAndScan(struct BrowserWindow *Parent, BPTR DirLock, BPTR RootLock, short Position)
{
	struct BrowserWindow *NewWin;

	if (!(NewWin = OpenBrowserWindow(Parent, DirLock, RootLock, Position))) {
		UnLock(DirLock);
		UnLock(RootLock);
		return;
	}
	if (!ScanDir(NewWin))
		CloseBrowserWindow(NewWin);
}


static BPTR GetRootLock(BPTR DirLock)
{
	char *sptr, buf[REQ_DSIZE];

	PathName(DirLock, buf, REQ_DSIZE-1);	/* Work on volume name. May be confusing, sorry */
	sptr = buf;
	while(*sptr && *sptr++ != ':');
	*sptr = '\0';
	return Lock(buf, ACCESS_READ);
}


/* Name and Type are from the ScrollEntry when not NULL */

static void DoDoubleClick(struct BrowserWindow *Win, struct ScrollEntry *S, char *Name, short Type)
{
	BPTR DirLock, RootLock;
	short Position;

	if (Type == DLX_FILE) {
		if (DoRun(S, Win->bw_DirLock, Win->bw_RootLock) && !(Config.Options & OPT_KEEPSELECTED))
			DeselectAll(NULL);
	}
	else {
		if (Win->bw_Type == BW_MAIN) {
			if (!(DirLock = Lock(Name, ACCESS_READ))) {
				SimpleRequest(ReqTitle, "Couldn't access \"%s\"\n%s.", Name, StrIoErr());
				return;
			}
			if (Type == DLX_ASSIGN) {
				if(!(RootLock = GetRootLock(DirLock))) {
					UnLock(DirLock);
					return;
				}
			}
			else
				RootLock = DupLock(DirLock);
			Position = POS_NEWDEV;
		}
		else {
			CurrentDir(Win->bw_DirLock);
			if(!(DirLock = Lock(Name, ACCESS_READ))) {
				SimpleRequest(ReqTitle, "Couldn't access \"%s\"\n%s.", Name, StrIoErr());
				CurrentDir(BrowserDir);
				return;
			}
			CurrentDir(BrowserDir);
			RootLock = DupLock(Win->bw_RootLock);
			Position = POS_SUBDIR;
		}		
		OpenAndScan(Win, DirLock, RootLock, Position);
		DeselectAll(NULL);
	}
}


void OpenParent(void)
{
	BPTR DirLock, RootLock;
	long err;

	if (!(DirLock = ParentDir(CurrentWin->bw_DirLock))) {
		if ((err = IoErr()) != ERROR_DEVICE_NOT_MOUNTED)
			SimpleRequest(ReqTitle, "Couldn't open window\n%s.", (err == 0) ? "No parent dir" : DosError(err));
		return;
	}
	RootLock = DupLock(CurrentWin->bw_RootLock);
	OpenAndScan(CurrentWin, DirLock, RootLock, POS_PARENTDIR);
}


void OpenDir(void)
{
	struct BrowserWindow *BW;
	static char buf[256];

	BW = (struct BrowserWindow *)WindowList.mlh_Head;
	SetWaitPointer(TRUE);
	if (GetString(buf, "Open Dir...", NULL, 40, 255))
		DoDoubleClick(BW, NULL, buf, DLX_ASSIGN);
	SetWaitPointer(FALSE);
}


void DoSelect(struct BrowserWindow *Win, struct ScrollEntry *S, UBYTE Options)
{
	if (S->se_State & STATE_DELETED)
		return;
	if (S->se_State & STATE_SELECTED) {
		if (Options & OPT_TOGGLESELECT) {
			S->se_State &= ~STATE_SELECTED;
			Win->bw_SelectBytes -= S->se_FileInfo.fi_Size;
			SelectBytes -= S->se_FileInfo.fi_Size;
			Win->bw_SelectBlocks -= S->se_FileInfo.fi_NumBlocks;
			SelectBlocks -= S->se_FileInfo.fi_NumBlocks;
			Win->bw_SelectNum--;
			SelectNum--;
		}
	}
	else {
		S->se_State |= STATE_SELECTED;
		Win->bw_SelectBytes += S->se_FileInfo.fi_Size;
		SelectBytes += S->se_FileInfo.fi_Size;
		Win->bw_SelectBlocks += S->se_FileInfo.fi_NumBlocks;
		SelectBlocks += S->se_FileInfo.fi_NumBlocks;
		Win->bw_SelectNum++;
		SelectNum++;
	}
	LastSelected = S;
}


static struct ScrollEntry *GetEntryAndPos(struct BrowserWindow *Win, short X, short Y, short *Line, short *Col)
{
	short index, L, C;

	if (Y <= 11 || Y >= Win->bw_ScrollStruct.NumLines * 9 + 11)
		return NULL;
	C = (X-4)/Win->bw_ColWidth;
	if (C > Win->bw_NumCol-1)
		return NULL;
	L = (Y-11)/9;
	index = L + Win->bw_ScrollStruct.NumLines*C + Win->bw_ScrollStruct.TopEntryNumber;
	if (index >= Win->bw_ShownEntries)
		return NULL;
	*Line = L;
	*Col = C;
	return Win->bw_EntryArray[index];
}


void SelectAll(void)
{
	short i;

	for( i=0 ; i<CurrentWin->bw_ShownEntries ; i++ )
		DoSelect(CurrentWin, CurrentWin->bw_EntryArray[i], Config.Options);
	RedrawEntries(CurrentWin);
	MakeBottomInfoString(CurrentWin);
	RefreshBottomInfo(CurrentWin);
}


void SelectMatch(void)
{
	struct ScrollEntry *S;
	short ten, nl, i;

	if (CurrentWin->bw_Type == BW_MAIN)
		return;
	SetWaitPointer(TRUE);
	if (FiltersReq(&Config.Select, SELECT_REQ)) {
		ten = CurrentWin->bw_ScrollStruct.TopEntryNumber;
		nl = CurrentWin->bw_ScrollStruct.NumLines;
		for( i=0 ; i<CurrentWin->bw_ShownEntries ; i++ ) {
			S = CurrentWin->bw_EntryArray[i];
			if (MatchFilters(&S->se_FileInfo, &Config.Select)) {
				DoSelect(CurrentWin, S, Config.Options);
				if (i >= ten && i < ten + nl * CurrentWin->bw_NumCol)
					Print(CurrentWin, S, 4+CurrentWin->bw_ColWidth*((i-ten)/nl), 18+((i-ten)%nl)*9);
			}
		}
		MakeBottomInfoString(CurrentWin);
		RefreshBottomInfo(CurrentWin);
	}
	SetWaitPointer(FALSE);
}


/* Deselect all but Current scrollentry */

void DeselectAll(struct ScrollEntry *Current)
{
	struct BrowserWindow *Win;
	struct ScrollEntry *S;
	short i,start,end;
	short x,y;

	Config.Select.si_Flags &= ~SI_AFFECT_SUBDIRS;		/* Deselect also files in subdirs */
	Win = (struct BrowserWindow *)WindowList.mlh_Head;
	while(Win->bw_Node.mln_Succ) {
		if (Win->bw_SelectNum > 0) {
			start = Win->bw_ScrollStruct.TopEntryNumber;
			end = MIN(start + Win->bw_NumCol * Win->bw_ScrollStruct.NumLines - 1, Win->bw_ShownEntries-1);
			for( i=0 ; i<Win->bw_ShownEntries ; i++ ) {
				S = Win->bw_EntryArray[i];
				if ((S->se_State & STATE_SELECTED) && S != Current) {
					S->se_State &= ~STATE_SELECTED;
					Win->bw_SelectBytes -= S->se_FileInfo.fi_Size;
					SelectBytes -= S->se_FileInfo.fi_Size;
					Win->bw_SelectBlocks -= S->se_FileInfo.fi_NumBlocks;
					SelectBlocks -= S->se_FileInfo.fi_NumBlocks;
					Win->bw_SelectNum--;
					SelectNum--;
					if (i < start || i > end)
						continue;
					x = 4 + Win->bw_ColWidth * ((i-start)/Win->bw_ScrollStruct.NumLines);
					y = 18 + 9 * ((i-start) % Win->bw_ScrollStruct.NumLines);
					Print(Win, S, x, y);
				}
			}
			MakeBottomInfoString(Win);
			RefreshBottomInfo(Win);
		}
		Win = (struct BrowserWindow *)Win->bw_Node.mln_Succ;
	}
}


void DoSelectDown(short X, short Y, long Sec, long Mic, USHORT Qual)
{
	static short *ChipPointerMatrix = NULL;
	static ULONG OldSec=0, OldMic=0;
	struct ScrollEntry *S;
	short line, col;
	BOOL ExtendedSelect;

	if (!ChipPointerMatrix) {
		ChipPointerMatrix = ArpAllocMem(CROSS_SIZE, MEMF_PUBLIC|MEMF_CHIP);
		CopyMem(PointerMatrix, ChipPointerMatrix, CROSS_SIZE);
	}
	ExtendedSelect = Qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT);
	if (!(S = GetEntryAndPos(CurrentWin, X, Y, &line, &col))) {
		if (!ExtendedSelect)
			DeselectAll(NULL);
		return;
	}
	if (S->se_State & STATE_DELETED)
		return;			/* forbid selection of a deleted entry */
	if ((S->se_State & STATE_SELECTED) && S == LastSelected && DoubleClick(OldSec, OldMic, Sec, Mic)) {
		SetWaitPointer(TRUE);
		DoDoubleClick(CurrentWin, S, S->se_FileInfo.fi_Name, S->se_FileInfo.fi_Type);
		SetWaitPointer(FALSE);
		return;
	}
	OldSec = Sec;
	OldMic = Mic;
	if (!ExtendedSelect)
		DeselectAll(S);
	DoSelect(CurrentWin, S, (Qual & IEQUALIFIER_CONTROL) ? OPT_TOGGLESELECT : Config.Options);
	if (S->se_State & STATE_SELECTED) {
		SetPointer(CurrentWin->bw_Window, ChipPointerMatrix, 16L, 16L, CROSS_XOFFSET, CROSS_YOFFSET);
		SelectDown = TRUE;
	}
	Print(CurrentWin, S, 4+CurrentWin->bw_ColWidth*col, 18+9*line);
	MakeBottomInfoString(CurrentWin);
	RefreshBottomInfo(CurrentWin);
}


void DoSelectUp(short X, short Y)
{
	struct BrowserWindow *Win;
	struct ScrollEntry *S;
	struct Window *W;
	struct Layer *Layer;
	struct HeadFileList *hfl;
	BPTR Dest, DestRoot = NULL;
	char buf[REQ_DSIZE];
	short sx,sy,l,c;	/* l & c not used. Just needed by GetEntryAndPos() */

	if (!SelectDown)	/* Handle selectup only if something has just been selected */
		return;
	SelectDown = FALSE;
	W = CurrentWin->bw_Window;
	ClearPointer(W);
	sx = X+W->LeftEdge;
	sy = Y+W->TopEdge;
	LockLayerInfo(&Screen->LayerInfo);
	Layer = WhichLayer(&Screen->LayerInfo, sx, sy);
	UnlockLayerInfo(&Screen->LayerInfo);
	if (Layer && (W = (struct Window *)Layer->Window)) {
		if (!(Win = FindWindow(W))) {
			SimpleRequest(ReqTitle, "Can't move files into this window.");
			return;
		}
		S = GetEntryAndPos(Win, sx - W->LeftEdge, sy - W->TopEdge, &l, &c);
		if (SelectNum && (!S || !(S->se_State & STATE_SELECTED)) && S != LastSelected) {
			if (Win == CurrentWin && (!(Config.Options & OPT_MOVEINTOSUB) || !S || S->se_FileInfo.fi_Type == DLX_FILE))
				return;
			/* now, we know there's something to do */
			if (!S || S->se_FileInfo.fi_Type == DLX_FILE || !(Config.Options & OPT_MOVEINTOSUB)) {
				/* Not over an object, nor a file, or over a dir but not move into subs */
				if (Win->bw_Type == BW_MAIN && !(Config.Options & OPT_MOVEINTOSUB))
					SimpleRequest(ReqTitle, "Can't move files into this window.");
				else if (hfl = MakeSelectedList(Win, DupLock(Win->bw_DirLock), DupLock(Win->bw_RootLock)))
					RunAction(hfl, BROWSERACTION_COPYMOVE);
			}
			else {
				/* dest is a dir which was visible in the BrowserWindow, move into sub is allowed */
				if (Config.Options & OPT_ASKBEFOREMOVE) {
					if (Win->bw_Type == BW_DIR) {
						strcpy(buf, (char *)Win->bw_Window->Title);
						TackOn(buf, S->se_FileInfo.fi_Name);
					}
					else
						strcpy(buf, S->se_FileInfo.fi_Name);
					if (!TwoGadRequest(ReqTitle, "Really move files into\n\"%s\" ?", buf))
						return;
				}
				if (Win->bw_Type == BW_DIR) {
					CurrentDir(Win->bw_DirLock);
					if (Dest = Lock(S->se_FileInfo.fi_Name, ACCESS_READ))
						DestRoot = DupLock(Win->bw_RootLock);
					CurrentDir(BrowserDir);
				}
				else {
					/* it is a volume, device, or assign, so no parent dir */
					if (Dest = Lock(S->se_FileInfo.fi_Name, ACCESS_READ)) {
						if (S->se_FileInfo.fi_Type == DLX_ASSIGN) {
							if (!(DestRoot = GetRootLock(Dest))) {
								UnLock(Dest);
								return;
							}
						}
						else	/* volume or device */
							DestRoot = DupLock(Dest);
					}
				}
				if (Dest && (hfl = MakeSelectedList(FindDir(Dest), Dest, DestRoot)))
					RunAction(hfl, BROWSERACTION_COPYMOVE);
			}
		}
	}
}


