implementation module EdDrawWindow;

//
// Functions for (re)drawing the screen
//

import StdClass,StdBool, StdInt, StdChar, StdString;
import deltaWindow, deltaTimer;

import EdTypes, EdDraw, EdText, EdProgramState, EdTextWindow, EdKeyboard;

(THEN) infixl;
(THEN) s f :== f s;

//
//	Draws line insertion/deletion of (new) current line in the front window on screen
//

DrawCurLine :: !EditWindow !LineUpdate !ProgState !IO -> ProgIO;
DrawCurLine	front=:{wtext=wtext`=:{text,cursorpos,curline,nrlines},wformat={metrics,winfont,tabw}}
			{	oldpos=old=:{CursorPos | x=ox,y=oy},
				newpos=new=:{CursorPos | x=nx,y=ny} }
			prog=:{editor=ed=:{editwindows}} io
	= AdjustActiveWindowFrame ScrollLine frame new EmptySelection metrics prog` io`;
	where {
	io`	= DrawInActiveWindow [	draw_line,
								draw_lines,
								draw_cursor ] io3;
	(frame,io3)				= ActiveWindowGetFrame io2;
	(prog`,io2)				= AdjustActiveWindowSize nrlines metrics prog1 io1;
	prog1					= {prog & editor={ed & editwindows=editwindows`}};
	io1						= DrawInActiveWindow [remove_cursor] io;
	editwindows`			= SetFrontWindow front` editwindows;
	front`					= {front & wtext = {wtext` & cursorpos = {cursorpos & vis=True}}};
	remove_cursor pic		= RemoveCursor old cht metrics.lead pic;
	draw_line pic | oy < ny	= erase_and_draw_line (erase_rest_curline pic);
							= draw_rest_curline pic;
	draw_lines pic			= Erase_and_DrawLines True (basey + metrics.height) metrics.height ofs tabw.ptabw right lines pic;
	draw_cursor pic			= DrawCursor new cht metrics.lead pic;
	erase_rest_curline pic	= EraseRestCurLine ox oy metrics.height right pic;
	erase_and_draw_line	pic	= Erase_and_DrawLine basey metrics.height ofs tabw.ptabw right line pic;
	draw_rest_curline pic	= DrawRestCurLine nx basey tabw.ptabw right curline.CurLine.after pic;
	(_,botr)				= frame;
	(right,bot)				= botr;
	basey					= ny + ofs;
	ofs						= metrics.ascent + metrics.lead;
	cht						= metrics.ascent+metrics.descent;
	line					= Line_GlueLine curline.CurLine.before curline.CurLine.after;
	lines					= Text_GetLines first number text;
	first					= inc curline.CurLine.lnr;
	number					= Between 0 (nrlines - first) nr;
	nr						= inc ((bot - PictureTop) / metrics.height) - curline.CurLine.lnr;
	};

//
//	Draws character insertion/deletion in current line of the front window  on screen
//

DrawCurChars :: !Char !EditWindow !LineUpdate !ProgState !IO -> ProgIO;
DrawCurChars	key front=:{wtext=wtext`=:{cursorpos,curline},wformat={metrics,tabw,winfont}}
				{ 	oldpos=old=:{CursorPos | x=ox,y=oy},
					newpos=new=:{CursorPos | x=nx,y=ny},
					width }
				prog=:{editor=ed=:{editwindows}} io
	= AdjustActiveWindowFrame ScrollLine frame new EmptySelection metrics prog` io`;
	where {
	io`			= DrawInActiveWindow
					[	remove_cursor, drawline, draw_cursor ] io1;
	prog`		= {prog & editor={ed & editwindows=editwindows`}};
	(frame,io1)	= ActiveWindowGetFrame io;
	editwindows`= SetFrontWindow front` editwindows;
	front`		= {front & wtext = {wtext` & cursorpos = {cursorpos & vis = True}}};
	drawline	= case key of
						{	BackSpKey	-> draw_backsp_cline;
							DelKey		-> draw_backsp_cline;
							TabKey		-> draw_tab_cline;				
							_			-> draw_shift_cline;
						};
	remove_cursor pic		= RemoveCursor old cht metrics.lead pic;
	draw_cursor	pic			= DrawCursor new cht metrics.lead pic;
	draw_backsp_cline pic	= DrawBackspCurLine	nx width ny
												metrics.height tabw.ptabw right
												curline.CurLine.after pic;
	draw_tab_cline pic		= DrawTabCurLine	ox nx oy
												metrics.height tabw.ptabw right
												curline.CurLine.after pic;
	draw_shift_cline pic	= DrawShiftCurLine	ox oy
												metrics.height tabw.ptabw right
												key curline.CurLine.after pic;
	cht					= metrics.ascent + metrics.descent;
	(right,_)			= botr;
	(_,botr)			= frame;
	};

//
//	Freezes the cursor on the screen/blinks the cursor
//
	
DrawFreezeCursor :: !Bool !ProgState !IO -> ProgIO;
DrawFreezeCursor freeze prog=:{editor=ed=:{editwindows}} io
	| freeze	= (prog`,DisableTimer TimerID io`);
				= (prog,EnableTimer TimerID io);
	where {
	io`				= DrawInActiveWindow [	RemoveCursor curpos cht metrics.lead,
											DrawCursor curpos cht metrics.lead ] io;
	prog`			= {prog & editor={ed & editwindows=editwindows`}};
	editwindows`	= SetFrontWindow front` editwindows;
	front`			= {front & wtext = {front.wtext & cursorpos={curpos & vis = True}}};
	(_,front) 		= GetFrontWindow editwindows;
	curpos			= front.wtext.cursorpos;
	metrics			= front.wformat.metrics;
	cht				= metrics.ascent + metrics.descent;
	};

//
// Draws text update in the front window after a major edit operation
//
	
DrawTextUpdate :: !EditWdId !ScrollMode !WindowUpdate !ProgState !IO -> ProgIO;
DrawTextUpdate wdid scroll_mode wu=:{added={l1,l2,c1,c2},nrremoved} prog=:{editor={editwindows}} io
	| no_lines_changed	= DrawCursorUpdate wdid scroll_mode frame wd wu prog io`;
	| one_line_changed	= DrawOneLineUpdate wdid scroll_mode frame wd wu prog io`;
						= DrawLinesUpdate wdid scroll_mode frame wd wu prog io`;
	where {
	no_lines_changed	= empty_added && nrremoved == 0;
	one_line_changed	= l2 == l1 && c1 <> c2 && nrremoved <= 1 || empty_added && nrremoved == 1;
	wd					= GetWindow wdid editwindows;
	(frame,io`)			= WindowGetFrame wdid io;
	empty_added			= l2 == l1 && c1 == c2;
	};

/* Scrolls active window such that the cursor is visable in the current window frame. */

AdjustActiveWindowFrame :: !ScrollMode !PictureDomain !CursorPos !Selection !FontMetr !ProgState !IO -> ProgIO;
AdjustActiveWindowFrame scroll_mode frame curpos selection metrics prog io
	| scroll_mode == ScrollNone			= (prog,io);
	| notscrollhor	&&	notscrollvert	= (prog,io);
	| notscrollhor						= ChangeActiveScrollBar (ChangeVThumb vtmb) prog io;
	|					notscrollvert	= ChangeActiveScrollBar (ChangeHThumb htmb) prog io;
										= ChangeActiveScrollBar (ChangeThumbs htmb vtmb) prog io;
	where {
	(notscrollhor, notscrollvert, htmb, vtmb)
		= NewThumbs scroll_mode frame curpos selection metrics
	};
	
/* Scrolls window such that the cursor is visable in the current window frame. */

AdjustWindowFrame :: !EditWdId !ScrollMode !PictureDomain !CursorPos !Selection !FontMetr !ProgState !IO -> ProgIO;
AdjustWindowFrame wdid scroll_mode frame curpos selection metrics prog io
	| scroll_mode == ScrollNone			= (prog,io);
	| notscrollhor	&&	notscrollvert	= (prog,io);
	| notscrollhor						= ChangeScrollBar wdid (ChangeVThumb vtmb) prog io;
	|					notscrollvert	= ChangeScrollBar wdid (ChangeHThumb htmb) prog io;
										= ChangeScrollBar wdid (ChangeThumbs htmb vtmb) prog io;
	where {
	(notscrollhor, notscrollvert, htmb, vtmb)
		= NewThumbs scroll_mode frame curpos selection metrics
	};

/*	Removes old cursor/selection, draws new cursor/selection, enables/disables
	timer, sets appropriate keyboard function. */

DrawCursorUpdate :: !EditWdId !ScrollMode !PictureDomain !EditWindow !WindowUpdate !ProgState !IO -> ProgIO;
DrawCursorUpdate	wdid scroll_mode frame
					wd=:{wtext=wtext`=:{cursorpos},wformat={metrics}}
					wu=:{oldsel,selection,oldpos,curpos}
					prog=:{editor=ed=:{editwindows}} io
	= AdjustWindowFrame wdid scroll_mode frame curpos selection metrics prog` io`;
	where {
	io`				= io	THEN SetKeyboardFunction_and_Timer wdid flash_cursor
							THEN DrawInWindow wdid [remove,draw];
	prog`			= {prog & editor={ed & editwindows=editwindows`}};
	editwindows`	= SetWindow wdid wd` editwindows;
	wd`				= {wd & wtext = {wtext` & cursorpos = {cursorpos & vis = flash_cursor}}};
	(remove,draw)	= Old_and_NewCursorSelection wu metrics;
	flash_cursor	= IsEmptySelection selection;
	};

/* Just one line has been changed and must be redrawn */

DrawOneLineUpdate :: !EditWdId !ScrollMode !PictureDomain !EditWindow !WindowUpdate !ProgState !IO -> ProgIO;
DrawOneLineUpdate	wdid scroll_mode frame=:((_,_),(right,_))
					wd=:{wtext=wtext`=:{curline,cursorpos,text},wformat={metrics,tabw}}
					wu=:{added={l1},curpos,selection}
					prog=:{editor=ed=:{editwindows}} io
	= AdjustWindowFrame wdid scroll_mode frame curpos selection metrics prog` io`
	where {
	io`					= io	THEN SetKeyboardFunction_and_Timer wdid flash_cursor
								THEN DrawInWindow wdid [remove,redraw_line,draw];
	prog`				= {prog & editor={ed & editwindows=editwindows`}};
	editwindows`		= SetWindow wdid wd` editwindows;
	wd`					= {wd & wtext = {wtext` & cursorpos = {cursorpos & vis = flash_cursor}}};
	redraw_line pic		= Erase_and_DrawLine basey metrics.height ofs tabw.ptabw right line pic;
	(remove,draw)		= Old_and_NewCursorSelection wu metrics;
	line | update_cline	= Line_GlueLine curline.CurLine.before curline.CurLine.after;
						= Text_GetLine l1 text;
	update_cline		= l1 == curline.CurLine.lnr;
	basey				= PictureTop + metrics.height * l1 + ofs;
	flash_cursor		= IsEmptySelection selection;
	ofs					= metrics.ascent + metrics.lead;
	};
	
/* More than 1 line needs redrawel */

DrawLinesUpdate :: !EditWdId !ScrollMode !PictureDomain !EditWindow !WindowUpdate !ProgState !IO -> ProgIO;
DrawLinesUpdate	wdid scroll_mode ((left,_),(right,bot))
				wd=:{wtext={cursorpos},wformat={metrics,tabw}}
				wu=:{ WindowUpdate | curpos,nrremoved,added={l1,l2},selection }
				prog=:{editor=ed=:{editwindows}} io
	= AdjustWindowFrame wdid scroll_mode frame` curpos selection metrics prog` io`;
	where {
	(frame`,io`)		= WindowGetFrame wdid io2;
	(prog`,io2)			= prog`_io2;
	prog`_io2 | same_nrlines
						= (prog1,io1);
						= AdjustWindowSize wdid nrlines metrics prog1 io1;
	io1					= io	THEN SetKeyboardFunction_and_Timer wdid flash_cursor
								THEN DrawInWindow wdid draw;
	draw | lines_removed= [erase,redraw_lines,redraw]
						= [remove,redraw_lines,redraw];
	prog1				= {prog & editor={ed & editwindows=editwindows`}};
	editwindows`		= SetWindow wdid wd` editwindows;
	wd`				 	= {wd1 & wtext = {wd1.wtext & cursorpos={cursorpos & vis = flash_cursor}}};
	(wd1,nrlines,text)	= GetText wd;
	erase				= EraseRectangle ((left,boty),(right,bot));
	redraw_lines pic	= Erase_and_DrawLines False basey metrics.height ofs tabw.ptabw right lines pic;
	(remove,redraw)		= Old_and_NewCursorSelection wu metrics;
	lines				= Text_GetLines start number text;
	start				= l1;
	number				= Between 0 (nrlines - start) nr;
	nr | same_nrlines	= nrremoved
						= inc ( (bot - PictureTop) / metrics.height - start );
	ldiff				= inc (l2 - l1) - nrremoved;
	basey				= PictureTop + l1 * metrics.height + ofs;
	ofs					= metrics.ascent + metrics.lead;
	boty				= PictureTop + (l1 + number) * metrics.height;
	flash_cursor		= IsEmptySelection selection;
	lines_removed		= ldiff < 0;
	same_nrlines		= ldiff == 0;
	};
	
SetKeyboardFunction_and_Timer :: !EditWdId !Bool !IO -> IO;
SetKeyboardFunction_and_Timer wdid enable io
	| frontid <> wdid	= io1;
	| enable			= EnableTimer TimerID io`;
						= DisableTimer TimerID io`;
	where {
	io`				= ChangeKeyboardFunction wdid TypeUndo io1;
	(_,frontid,io1)	= GetActiveWindow io;
	};

//
// Redraws the window indicated by 1st arg.
//

DrawUpdateWindow :: !EditWdId !(EditWindow -> EditWindow) !ProgState !IO -> ProgIO;
DrawUpdateWindow wdid update prog=:{editor=ed=:{editwindows}} io
	= (prog`, ChangeUpdateFunction wdid (UpdateWindow wdid) io`);
	where {
	(prog`,io`)				= DrawInWindowFrame wdid (DrawTheLines newwd) prog2 io7;
	(prog2,io7)				= ChangeScrollBar wdid (ChangeVBar verttmb metrics.height) prog1 io6;
	io6						= DrawInWindow wdid [EraseRectangle frame] io5;
	io5						= DrawInWindow wdid [SetFont winfont.font] io4;
	(frame,io4)				= WindowGetFrame wdid io3;
	(prog1,io3)				= ChangePictureDomain wdid picdom prog0 io2;
	prog0					= {prog & editor={ed & editwindows=editwindows`}};
	io2						= ChangeUpdateFunction wdid NoUpdates io1;
	io1 | editable			= SetKeyboardFunction_and_Timer wdid flash_cursor io;
							= io;
	editwindows`			= SetWindow wdid newwd editwindows;
	oldwd					= GetWindow wdid editwindows;
	newwd					= update oldwd;
	picdom					= ((PictureLeft,PictureTop),(PictureRight,bottom));
	bottom | MinBottom>bot`	= MinBottom;
							= bot`;
	bot`					= PictureTop + nrlines * metrics.height;
	verttmb					= Between PictureTop bottom thumb;
	thumb					= PictureTop + metrics.height * ((top - PictureTop) / oldheight);
	(topl,_)				= frame;
	(_,top)					= topl;
	editable				= wdtype == EditWd || wdtype == TypeWd || wdtype == ErrorWd;
	flash_cursor			= IsEmptySelection selection;
	oldheight				= oldwd.wformat.metrics.height;
	winfont					= newwd.wformat.winfont;
	nrlines					= newwd.wtext.nrlines;
	metrics					= newwd.wformat.metrics;
	wdtype					= newwd.wstate.wdtype;
	selection				= newwd.wtext.WinText.selection;
	};
	
//
//	Scroll the one window up or down or to the beginning or end of text;
//

DrawScrollWindow :: !EditWdId !Char !ProgState !IO -> ProgIO;
DrawScrollWindow wdid key prog=:{editor={editwindows}} io
	| key == PgUpKey	&& scroll_up	= ChangeScrollBar wdid (ChangeVThumb page_up) prog io`;
	| key == PgDownKey	&& scroll_down	= ChangeScrollBar wdid (ChangeVThumb page_down) prog io`;
	| key == BeginKey	&& scroll_up	= ChangeScrollBar wdid (ChangeVThumb begin) prog io`;
	| key == EndKey		&& scroll_down	= ChangeScrollBar wdid (ChangeVThumb end) prog io`;
										= (prog,io`);
	where {
	(frame,io`)		= WindowGetFrame wdid io;
	scroll_up		= top <> PictureTop;
	scroll_down		= top < PictureTop + height * linenr;
	page_up			= top - (bot - top);
	page_down		= top + (bot - top);
	begin			= PictureTop;
	end				= PictureTop + height * (inc linenr) - (bot - top);
	(topl,botr)		= frame;
	(_,top)		= topl;
	(_,bot)			= botr;
	wd				= GetWindow wdid editwindows;
	linenr			= dec wd.wtext.nrlines;
	height			= wd.wformat.metrics.height;
	};

//
// Selects/unselects text in a window
//	

DrawSelect :: !Bool !EditWdId !WindowUpdate !ProgState !IO -> ProgIO;
DrawSelect	goto_cursor wdid wu=:{curpos,selection} prog=:{editor=ed=:{editwindows}} io
	| not goto_cursor		= (prog`, ioa);
	| nothor	&&	notvert	= (prog`, iob);
	| nothor				= ChangeScrollBar wdid (ChangeVThumb vtmb) prog` iob;
	|				notvert	= ChangeScrollBar wdid (ChangeHThumb htmb) prog` iob;
							= ChangeScrollBar wdid (ChangeThumbs htmb vtmb) prog` iob;
	where {
	(nothor,notvert,htmb,vtmb)
						= NewThumbs ScrollHalfWin frame curpos selection metrics;
	(frame,iob)			= WindowGetFrame wdid ioa;
	ioa					= DrawInWindow wdid [remove,draw] io1;
	io1					= SetKeyboardFunction_and_Timer wdid flash_cursor io;
	prog`				= {prog & editor={ed & editwindows=editwindows}};
	wd					= GetWindow wdid editwindows; 
	(remove,draw)		= Old_and_NewCursorSelection wu metrics;
	flash_cursor		= IsEmptySelection selection;
	metrics				= wd.wformat.metrics;
	};

//
// The update function for the text windows
//

UpdateWindow :: !EditWdId !UpdateArea !ProgState -> (!ProgState, ![DrawFunction]);
UpdateWindow wdid areas prog=:{editor=ed=:{editwindows}}
	=  (prog`, [update]);
	where {
	prog`			= {prog & editor={ed & editwindows=editwindows`}};
	editwindows`	= SetWindow wdid wd editwindows;
	wd				= GetWindow wdid editwindows;
	update			= RedrawTheAreas wd areas;
	};

/*	Special Update function to disable updating of windows */
	
NoUpdates :: !UpdateArea !ProgState -> (!ProgState, ![DrawFunction]);
NoUpdates area editor =  (editor, []);

/* The function for drawing visable lines in the window. */

DrawTheLines :: !EditWindow !UpdateArea !ProgState -> (!ProgState, ![DrawFunction]);
DrawTheLines wd areas prog
	= (prog, [EraseTheAreas areas, RedrawTheAreas wd areas]);
	
RedrawTheAreas :: !EditWindow !UpdateArea -> DrawFunction;
RedrawTheAreas {	wtext={text,curline,nrlines,cursorpos,selection},
					wformat={tabw={ptabw},metrics={ascent,descent,height,lead}} }
				areas
	= RedrawAreas ptop height cht lead ptabw nrlines text` cursorpos selection areas areas;
	where {
	(_,text`,_)	= EW_ResetCurLine False text curline;
	ptop		= PictureTop + ascent + lead;
	cht			= ascent + descent;
	};
	
EraseTheAreas :: !UpdateArea !Picture -> Picture;
EraseTheAreas [area : rest] pict =  EraseTheAreas rest (EraseRectangle area pict);
EraseTheAreas no_areas      pict =  pict;

/*	Auxiliary functions for UpdateWindow */

RedrawAreas	:: !Int !Int !Int !Int !Int !NrLines !Text !CursorPos !Selection !UpdateArea !UpdateArea !Picture -> Picture;
RedrawAreas ptop height cht lead tabwidth nrlines text curpos sel=:{psel} [] areas pic
	| IsEmptySelection sel
		=  RemoveCursor curpos cht lead pic;
		=  RedrawSelection psel height areas pic;
RedrawAreas ptop height cht lead tabwidth nrlines text curpos sel [((left,top),(rght,bot)):rest] areas pic
	=  RedrawAreas ptop height cht lead tabwidth nrlines text curpos sel rest areas drawlines;
	where {
	drawlines	= DrawLines yoffset height tabwidth rght lines pic;
	yoffset		= ptop +  first * height ;
	lines		= Text_GetLines first number text;
	first		= Between 0 (dec nrlines) ((top - PictureTop) / height);
	number		= Between 0 (nrlines - first) ( (bot - top) / height + 2 );
	};

RedrawSelection	:: !PartPSel !Int !UpdateArea !Picture -> Picture;
RedrawSelection selection height [] pic =  pic;
RedrawSelection selection height [area:areas] pic
	=  RedrawSelection selection height areas (DrawReSelect area selection height pic);
	
//
// Determines mouse position when the mouse button is still down and the mouse is dragged outside
// the window frame
//

DragOutsideFrame :: !MouseState !ProgState !IO -> (!ProgState,!IO,!Int,!Int);
DragOutsideFrame ((mx,my),mode,mod_new) prog=:{editor={editwindows}} io
	| out_right || out_bottom || out_top || out_left	= (prog`,io`,mx`,my`);
														= (prog,io1,mx,my);
	where {
	(prog`,io`)			= ChangeActiveScrollBar scrollv prog2 io2;
	(prog2,io2)			= ChangeActiveScrollBar scrollh prog io1;
	(_,front)			= GetFrontWindow editwindows;
	(frame,io1)			= ActiveWindowGetFrame io;
	(lft,top)			= topl;
	(rgt,bot)			= botr;
	(topl,botr)			= frame;
	mx` | out_right		= rgt + HorScroll;
		| out_left		= lft - HorScroll;
						= mx;
	my` | out_bottom	= bot + metrics.height;
		| out_top		= top - metrics.height;
						= my;
	scrollv | out_bottom= ChangeVThumb (top + metrics.height);
			| out_top	= ChangeVThumb (top - metrics.height);
						= ChangeVThumb top;
	scrollh	| out_right	= ChangeHThumb (lft + HorScroll);
			| out_left	= ChangeHThumb (lft - HorScroll);
						= ChangeHThumb lft;
	out_left			= mx < lft;
	out_top				= my < top;
	out_right			= mx > rgt;
	out_bottom			= my > bot;
	metrics				= front.wformat.metrics;
	};

::	DrawFunctionType :== Picture -> Picture;

/*	Aux function: returns drawing functions for removing old cursor/selection and drawing new
	cursor/selection */
	
Old_and_NewCursorSelection ::	!WindowUpdate !FontMetr
								-> (DrawFunctionType, DrawFunctionType);
Old_and_NewCursorSelection	{oldpos,curpos,oldsel,selection,added={l1,c1,l2,c2},nrremoved}
							{ascent,descent,height,lead}
	| IsEmptySelection oldsel || IsEmptySelection selection || text_changed
		= (remove_old_cursor_or_selection, draw_new_cursor_or_selection);
		= (DrawHilite oldsel selection height, \x->x);
	where { 
	remove_old_cursor_or_selection pic
		| IsEmptySelection oldsel		= RemoveCursor oldpos cht lead pic;
										= DrawReHilite oldsel height pic;
	draw_new_cursor_or_selection pic
		| IsEmptySelection selection	= DrawCursor curpos cht lead pic;
										= DrawReHilite selection height pic;
	text_changed						= not (l1 == l2 && c1 == c2 && nrremoved == 0);
	cht									= ascent + descent;
	};

/* Aux function: determines new horizontal and vertical thumb values */

NewThumbs :: !ScrollMode !PictureDomain !CursorPos !Selection !FontMetr -> (!Bool, !Bool, !Int, !Int);
NewThumbs scroll_mode	((left,top),(right,bot)) {CursorPos | x,y}
						sel=:{psel={bx,by,ex,ey}} {ascent,descent,height}
	= (notscrollhor, notscrollvert, htmb, vtmb);
	where {
	notscrollhor			=	half_win &&
									(	(left < bx` && bx` < right) ||
										(left < ex` && ex` < right) ) || 
								not half_win &&
									(	left < x` && x` < right );
	notscrollvert			= 	half_win &&
									(	(top <= by` && by` + height <= bot) ||
										(top <= ey` && ey` + height <= bot) ) ||
								not half_win &&
									(	top <= y` && y` + height <= bot );
	htmb | x` < PictureLeft + hwidth
							= PictureLeft;
							= x` - hwidth / 2;
	vtmb					= case scroll_mode of
								{	ScrollLine | y` < top		-> y`;
									ScrollLine					-> y` - height * dec ((bot - top) / height); 
									ScrollHalfWin				-> y` - (bot - top) / 2;
									ScrollFullWin | y` < top	-> y`;					
									ScrollFullWin				-> y` - height * dec ((bot - top) / height);
								};
	hwidth					= right - left;
	no_selection			= IsEmptySelection sel;
	(bx`,by`,ex`,ey`)		= bx`_by`_ex`_ey`;
	bx`_by`_ex`_ey` | no_selection
							= (x,y,x,y);
							= (bx,by,ex,ey);
	(x`,y`)					= x`_y`;
	x`_y`
		| no_selection		= (x,y);
		| x == bx && y == by= (bx,by);
							= (ex,ey);
	half_win				= scroll_mode == ScrollHalfWin;
	};
	
/*	Aux function: adjusts the size of the active PictureDomain when the number of lines changes */

AdjustActiveWindowSize :: !NrLines !FontMetr !ProgState !IO -> ProgIO;
AdjustActiveWindowSize nrlines {height} prog io
	=  ChangeActivePictureDomain picdom prog io;
	where {
	picdom					= ((PictureLeft,PictureTop),(PictureRight,bottom));
	bottom
		| MinBottom > bot	= MinBottom;
							= bot;
	bot						= PictureTop + nrlines * height;
	};
	
/*	Aux function: adjusts the size of the PictureDomain when the number of lines changes */

AdjustWindowSize :: !EditWdId !NrLines !FontMetr !ProgState !IO -> ProgIO;
AdjustWindowSize wdid nrlines {height} prog io
	=  ChangePictureDomain wdid picdom prog io;
	where {
	picdom					= ((PictureLeft,PictureTop),(PictureRight,bottom));
	bottom
		| MinBottom > bot	= MinBottom;
							= bot;
	bot						= PictureTop + nrlines * height;
	};
