implementation module EdMouse;

/*	Mouse handling routines for the editor */

import StdClass,StdInt, StdString, StdBool, StdChar, StdTuple;
import deltaEventIO, deltaWindow, deltaTimer, deltaMenu;

import EdProgramState, EdTextWindow, EdDrawWindow, EdMenuItems, EdSupport, EdSearchMenu;

//
//	The mouse functions for the edit windows
//

/*	Track is the mouse-function for the editor */

Track :: !MouseState !ProgState !IO -> ProgIO;

Track (pos,ButtonUp,mod_new) prog io =  (prog,io);

Track	mstate=:(pos,buttondown,mod_new=:(shiftdown,_,comd,_))
		prog=:{editor=ed=:{editwindows}} io
	| ignore									= (prog, io);
	| shiftdown	&&	no_selection	&& not comd	= Drag (pos,ButtonStillDown,mod_new) proga ioa;
	| shiftdown						&& not comd	= Drag (pos,ButtonStillDown,mod_new) progb iob;
	|				no_selection				= TrackW mstate progc ioc;
												= TrackW mstate progd iod;
	where {
	(proga,ioa)		= DrawFreezeCursor True prog iob;
	
	progb			= {prog & editor={ed & editwindows=SetFrontWindow frontb editwindows}};
	iob				= ChangeActiveMouseFunction Drag io;
	frontb			= SetActiveCursorPos top_left front;
	
	(progc,ioc)		= DrawFreezeCursor False prog io;
	
	(progd,iod)		= Edit_UpdateMenuItems prog3d io2d;
	(prog3d,io2d)	= DrawTextUpdate frontid ScrollNone wu prog2d io1d;
	(prog2d,io1d)	= DrawFreezeCursor False prog1d io;
	prog1d			= {prog & editor={ed & editwindows=SetFrontWindow frontd editwindows}};
	(frontd,wu)		= UnselectText top_left front;
	
	(frontid,front)	= GetFrontWindow editwindows;
	selection		= front.wtext.WinText.selection;
	no_selection	= IsEmptySelection selection;
	top_left		= ShiftTrackCursorPos pos selection;
	wdtype			= front.wstate.wdtype;
	ignore			= wdtype == ClpbrdWd || wdtype == ProjectWd;
	};

/* Determines whether the selection is extended towards the top left side */

ShiftTrackCursorPos	:: (!Int,!Int) !Selection -> Bool;
ShiftTrackCursorPos (mx,my) {tsel={l1,c1,l2,c2},psel={bx,by,ex,ey}}
	= bydif < eydif || (bydif == eydif && bxdif < exdif);
	where {
	bydif= abs (my - by); eydif= abs (my - ey);
	bxdif= abs (mx - bx); exdif= abs (mx - ex);
	};
	
/*	TrackW is called when there is no selection (anymore) */

TrackW :: !MouseState !ProgState !IO -> ProgIO;
TrackW mstate=:(mpos=:(mx,my),button,(shiftdown,_,comd,_)) prog=:{editor=ed=:{editwindows}} io
	| find		= FindDefOrImp (not shiftdown) proga ioa;
	| select 	= (proga,ioa);
	| setcurpos	= DrawTextUpdate frontid ScrollLine wub progb io;
	| freeze	= Drag mstate progc ioc;
				= (prog, io);
	where {
	(proga,ioa)		= Edit_UpdateMenuItems prog3 io3;
	(prog3,io3)		= DrawTextUpdate frontid ScrollLine wua prog2 io2;
	prog2			= {prog & editor={ed &editwindows=SetFrontWindow fronta editwindows}};
	(fronta,wu2)	= DirectionKey frame True mod2 key front2a;
// RWS ...
	front2a
		| IsEmptyList front.wtext.curline.CurLine.before
			=	front1a;
		| otherwise
			=	fst (DirectionKey frame False mod1 LeftKey front1a);
//	(front2a,_)		
//					= DirectionKey frame False mod1 LeftKey front1a;
// ... RWS
	(front1a,wu1)	= GotoMousePosition False mx my front;
	(frame,io2)		= ActiveWindowGetFrame io1;
	io1				= ChangeActiveMouseFunction TrackD io;
	(mod1,mod2,key)	= case button of
						 {	ButtonDoubleDown	-> (Optn,Optn,RightKey);
						 	ButtonTripleDown	-> (Comd,None,DownKey);
						 	_					-> (None,None,RightKey); // don't care
						 };
	wua				= {WindowUpdate | wu2 & oldpos=wu1.WindowUpdate.oldpos, oldsel=wu1.oldsel};
						 
	progb			= {prog & editor={ed & editwindows=SetFrontWindow frontb editwindows}};
	(frontb,wub)	= GotoMousePosition False mx my front;
	
	(progc,ioc)		= DrawFreezeCursor True prog (ChangeActiveMouseFunction Drag io);
	
	(find,select,setcurpos,freeze)
					= case button of
						{	ButtonDoubleDown | comd	-> (True,False,False,False);
							ButtonDoubleDown		-> (False,True,False,False);
							ButtonTripleDown		-> (False,True,False,False);
							ButtonDown				-> (False,False,True,False);
							ButtonStillDown			-> (False,False,False,True);
						/*	ButtonUp */	_			-> (False,False,False,False);
						};
	(frontid,front)	= GetFrontWindow editwindows;
	};

/*	TrackD is the mouse-function after a double-click or a triple-click
	It filters out the ButtonStillDown events between clicks */

TrackD :: !MouseState !ProgState !IO -> ProgIO;
TrackD (pos,ButtonStillDown,mod_new) prog io =  (prog,io);
TrackD mstate prog io =  (prog, ChangeActiveMouseFunction Track io);

/* Drag is the mouse-function that is used while the user is dragging the
	mouse to select a piece of text */
	
Drag :: !MouseState !ProgState !IO -> ProgIO;
Drag mstate=:(pos=:(x,y), button, mod_new) prog=:{editor=ed=:{editwindows}} io
	| still_down	= DrawTextUpdate frontid ScrollLine wua proga ioa;
	| up			= Edit_UpdateMenuItems progb iob;
					= (prog,io);
	where {
	proga				= setproga prog1a;
	(prog1a,ioa,mx,my)	= DragOutsideFrame mstate prog io;
	(fronta,wua)		= GotoMousePosition True mx my front;
	
	(progb,iob)			= DrawTextUpdate frontid ScrollLine wub prog1b io1b;
	io1b				= ChangeActiveMouseFunction Track io;
	prog1b				= {prog & editor={ed & editwindows=SetFrontWindow frontb editwindows}};
	(frontb,wub)		= GotoMousePosition True x y front; 
	
	(still_down,up)		= case button of
							{	ButtonStillDown	-> (True,False);
								ButtonUp		-> (False,True);
								_				-> (False,False);
							};
	(frontid,front)		= GetFrontWindow editwindows;
	
	setproga :: !ProgState -> ProgState;
	setproga prog=:{editor=ed=:{editwindows}}
		= {prog & editor={ed & editwindows=SetFrontWindow fronta editwindows}};
	};	

/* Drag is the mouse-function that is used while the user is dragging the
	mouse to select a piece of text after double clicking */
	
Drag2 :: !MouseState !ProgState !IO -> ProgIO;
Drag2 mstate=:(pos=:(x,y), button, mod_new) prog=:{editor=ed=:{editwindows}} io
	| still_down	= DrawTextUpdate frontid ScrollLine wua proga ioa;
	| up			= Edit_UpdateMenuItems progb iob;
					= (prog,io);
	where {
	proga				= setproga prog1a;
	(prog1a,ioa,mx,my)	= DragOutsideFrame mstate prog io;
	(fronta,wua)		= GotoMousePosition True mx my front;
	
	(progb,iob)			= DrawTextUpdate frontid ScrollLine wub prog1b io1b;
	io1b				= ChangeActiveMouseFunction Track io;
	prog1b				= {prog & editor={ed & editwindows=SetFrontWindow frontb editwindows}};
	(frontb,wub)		= GotoMousePosition True x y front; 
	
	(still_down,up)		= case button of
							{	ButtonStillDown	-> (True,False);
								ButtonUp		-> (False,True);
								_				-> (False,False);
							};
	(frontid,front)		= GetFrontWindow editwindows;
	
	setproga :: !ProgState -> ProgState;
	setproga prog=:{editor=ed=:{editwindows}}
		= {prog & editor={ed & editwindows=SetFrontWindow fronta editwindows}};
	};	
