implementation module EdMenuItems;

import StdClass, StdBool, StdString, StdInt,StdArray, StdList;
import deltaMenu, deltaEventIO, deltaWindow, deltaTimer;

import EdProgramState, EdTypes, EdText, EdTextFind, EdPath, EdWindows, EdConstants, EdLists, EdParse;
import EdFileMenu, EdCleanSystem;

//
// Operations for updating the menu bar
//

//
//	updates the menu items after an edit operation has taken place.
//	i.e. after actions selected from the edit/search menu's or mouse/keyboard events.
//

(THEN) infixl;
(THEN) s f :== f s;
	
Edit_UpdateMenuItems :: !ProgState !IO -> ProgIO;
Edit_UpdateMenuItems prog=:{editor={findinfo,editwindows,clipboard}} io
	= (prog, io`);
	where {
	io`	= io	THEN AbleFindItems wdtype error has_sel sel_text findinfo
				THEN AbleFileItems wdtype dcl_icl saved has_sel sel_text
				THEN AbleEditItems wdtype front.wstate.undoinfo.menu has_selection clipboard
				THEN EnableMenuItems window_items;

	error					= WindowPresent ErrorWdID editwindows;
	(_,front)				= GetFrontWindow editwindows;
	selection				= front.wtext.WinText.selection;
	wdtype					= front.wstate.wdtype;
	saved					= front.wstate.saved || wdtype <> EditWd;
	path					= front.wstate.pathname;
	window_items | saved	= []
							= [ISaveaID];
	dcl_icl					= IsDefPathname path || IsImpPathname path;
	has_selection			= not (IsEmptySelection selection);
	(has_sel,sel_text)		= FindSelection selection front.wtext.curline;
	};

//
//	updates the menu items after a new window has been opened
//

Opened_UpdateMenuItems :: !EditWdId !Int !EditWdId !String !ProgState !IO -> ProgIO;
Opened_UpdateMenuItems oldid index newid title 
                       prog=:{editor={Editor | project,editwindows}} io
 = (prog,io`);
	where {
	io`				= EnableMenuItems enableIDs io4;
	io4	| new_clip	= io3;
		| new_prj	= EnableMenuItems prj_enable (DisableMenuItems prj_disable (ChangeMenuItemTitles prj_change io3));
					= MarkMenuItems [newid] io3;
	io3 | old_clip 	= io2;
		| new_prj	= io2;
					= UnmarkMenuItems [oldid] io2;
	io2 | new_clip	= io1;
		| new_prj	= InsertMenuItems IWinPrjID index [WindowItem newid title] io1;
					= InsertMenuItems IWindoID index [WindowItem newid title] io1;
	io1 | new_clip  = ChangeMenuItemTitles clip_change io;
					= io;
 enableIDs = if (EditWindowsPresent editwindows)
				[ICloseID,IClosaID,INextWID:IfUnixSystem [] [IPrintID]]
				[];
	new_clip		= newid == ClpbrdWdID;
	new_prj			= newid == ProjectWdID;
	old_clip		= oldid == ClpbrdWdID || oldid == NoWdID;
	clip_change		= [(IShowCID,"Hide Clipboard")];
	prj_enable		= [IExecuID,IBriUDID,ISetPrID,ICloPrID,ILinkOptionsID];
	prj_disable		= [IOpePrID,INewPrID];
	prj_change		= [(IExecuID,"Run '" +++ PR_GetRootModuleName project +++ "'")];
	};
	
EditWindowsPresent :: !EditWindows -> Bool;
EditWindowsPresent editwindows
      = not (IsEmptyList (Filter (\(wid,_) -> wid<>ProjectWdID) editwindows));
// if there is only the ProjectWdId in the editwindows list, then there 
// "is no window present" (MW)  

WindowItem id name :== CheckMenuItem id name NoKey Able NoMark (SetThisWindow id EmptyTSel);

//
//	updates the menu items after a window has been closed
//
	
Closed_UpdateMenuItems :: !Bool !EditWdId !ProgState !IO -> ProgIO;
Closed_UpdateMenuItems front_closed wdid prog=:{editor={editwindows}} io
 | not windows_present	= (prog, NoWindows io2);
	| front_closed			= Window_UpdateMenuItems wdid frontid prog` io`;
							= ClosedProjectUpdateItems wdid prog` io`; 
	where {
	(prog`,io`)		= ClosedSavedWindows prog io2;
	io1 = DisableItemsIfEditWindowsPresent editwindows io;
 io2				= ClosedClipboardUpdateMenuItems wdid io1;
	(frontid,_)		= GetFrontWindow editwindows;
	windows_present	= WindowsPresent editwindows;
	};

DisableItemsIfEditWindowsPresent :: !EditWindows !IO -> IO;
DisableItemsIfEditWindowsPresent editwindows io
 | EditWindowsPresent editwindows
    = io;
    = DisableMenuItems [ICloseID,IClosaID,INextWID:IfUnixSystem [] [IPrintID]] io;
	
ClosedSavedWindows :: !ProgState !IO -> ProgIO;
ClosedSavedWindows prog=:{editor={editwindows}} io
	| saved	= (prog, DisableMenuItems [ISaveaID] io);
			= (prog, io);
	where {
	saved		= SavedWindows editwindows;
	};

ClosedClipboardUpdateMenuItems :: !EditWdId !IO -> IO;
ClosedClipboardUpdateMenuItems wdid io
	| wdid == ClpbrdWdID	= ChangeMenuItemTitles [(IShowCID,"Show Clipboard")] io;
							= RemoveMenuItems [wdid] (DisableTimer TimerID io);

ClosedProjectUpdateItems :: !EditWdId !ProgState !IO -> ProgIO;
ClosedProjectUpdateItems wdid prog=:{editor={execwdid,editwindows}} io
	| wdid == ProjectWdID	= (prog,io`);
							= (prog,io);
	where {
	io`					= io	THEN EnableMenuItems enable`
								THEN DisableMenuItems disable`
								THEN RemoveMenuItems [ProjectWdID]
								THEN ChangeMenuItemTitles titles`;
	(_,front)			= GetFrontWindow editwindows;
	titles` | mainset	= [(IExecuID,"Run '" +++ mn +++ "'")];
						= [(IExecuID,"Run")];
	enable` | icl		= [INewPrID,IExecuID,IBriUDID : enable1];
						= enable1;
	enable1 | mainset	= [IExecuID,IBriUDID : enable];
						= enable;
	enable				= [IOpePrID];
	disable` | icl		= disable1;
						= [INewPrID,IExecuID,IBriUDID : disable1];
	disable1 | mainset	= disable;
						= [IExecuID,IBriUDID : disable];
	disable				= [ISetPrID,ICloPrID,ILinkOptionsID];
	mainset				= execwdid <> NoWdID;
	mn					= GetModuleName (GetWindow execwdid editwindows).wstate.pathname;
	path				= front.wstate.pathname;
	icl					= IsImpPathname path;
	};

//	updates the menu items after a new window has been set in front.

Window_UpdateMenuItems :: !EditWdId !EditWdId !ProgState !IO -> ProgIO;
Window_UpdateMenuItems	oldid newid
						prog=:{editor={editwindows,project,execwdid,clipboard,findinfo}/*,interpreterState=interpreterState0 */} io
	= (prog /* & interpreterState=interpreterState */,io`);
	where {
/*	interpreterState = interpreterState0;

	(is_loaded,interpreterState) = loaded interpreterState0;

	loaded NotLoaded
		=	(False,NotLoaded);
	loaded l
		=	(True,l);
*/
	io`	= io	THEN AbleFindItems wdtype error has_sel sel_text findinfo
				THEN AbleEditItems wdtype front.wstate.undoinfo.menu has_selection clipboard
				THEN AbleFileItems wdtype dcl_icl saved has_sel sel_text
				THEN AbleProjectItems wdtype pset execfront
				THEN AbleCommandItems wdtype pset mainset execfront execpath
				THEN AbleWindowItems wdtype oldid` newid
				THEN AbleTimer wdtype has_selection;
	pset				= PR_ProjectSet project;
	mainset				= execwdid <> NoWdID;
	error				= WindowPresent ErrorWdID editwindows;
	(_,front)			= GetFrontWindow editwindows;
	present				= WindowPresent oldid editwindows;
	path				= front.wstate.pathname;
	selection			= front.wtext.WinText.selection;
	wdtype				= front.wstate.wdtype;
	execpath | pset 	= PR_GetRootModuleName project;
						= GetModuleName (GetWindow execwdid editwindows).wstate.pathname;
	execfront			= IsImpPathname path;
	oldid` | present	= oldid;
						= NoWdID;
	saved				= front.wstate.saved || wdtype <> EditWd;
	dcl_icl				= IsDefPathname path || IsImpPathname path;
	has_selection		= not (IsEmptySelection selection);
	(has_sel,sel_text)	= FindSelection selection front.wtext.curline;
	};
	
//
//	updates the menu items after the front window or project has been saved. */
//

Saved_UpdateMenuItems :: !Pathname !ProgState !IO -> ProgIO;
Saved_UpdateMenuItems oldpath prog=:{editor={editwindows}} io
	| saved			= (prog, DisableMenuItems [ISaveaID] io`);
					= (prog, io`);
	where {
	saved			= SavedWindows editwindows;
	(frontid,front)	= GetFrontWindow editwindows;
	io`	| diff_path	= io	THEN DisableMenuItems [ISaveID, IReverID]
							THEN ChangeMenuItemTitles [(frontid,name)]
							THEN ChangeWindowTitle frontid name
					= DisableMenuItems [ISaveID, IReverID] io;
	newpath			= front.wstate.pathname;
	name			= RemovePath newpath;
	diff_path		= oldpath <> newpath;
	};
	
SavedWindows :: !EditWindows -> Bool;
SavedWindows Nil = True;
SavedWindows ((_,wd=:{wstate={saved,wdtype}}):!wds)
	| saved || wdtype <> EditWd	= SavedWindows wds;
								= False;

//		
//	updates the menu items after the text in the front window has been changed due to a key
//	stroke.
//
	
Changed_UpdateMenuItems :: !ProgState !IO -> ProgIO;
Changed_UpdateMenuItems prog=:{editor} io
	= (prog, EnableMenuItems [ISaveID,IReverID,ISaveaID] io);

//	
//	updates the menu items after an undoable action was followed by typing text
//
	
Undo_UpdateMenuItems :: !ProgState !IO -> ProgIO;
Undo_UpdateMenuItems prog io
	= (prog, io THEN EnableMenuItems [IUndoID] THEN ChangeMenuItemTitles [(IUndoID,"Undo Typing")]);
								
/* Aux. function: Sets menu items to the situation that all windows are closed */

NoWindows :: !IO -> IO;
NoWindows io
	= io THEN EnableMenuItems
			[	IOpDefID, IOpImpID, IOpePrID, IShowCID ]
		THEN DisableMenuItems
			[	ICloseID, ISwapID, ISaveID, ISavesID, IReverID,
				IUndoID, ICutID, ICopyID, IShiftLeftID, IShiftRightID, IPasteID, IClearID, IBalanID, ISelecID, IFormaID,
				IFindID, IFindNID, IFindSID, IReplaID, IFindEID, IFindDID, IFindIID, IFindIdID, IGotoLID, IGotoCID,
				ICompiID, ICheckID, IGenerID, IExecuID, IBriUDID,
				INewPrID, ISetPrID, ICloPrID, ILinkOptionsID,
				ISaveaID, IClosaID, INextWID : IfUnixSystem [] [IPrintID]]
		THEN ChangeMenuItemTitles
			[	(IOpDefID,"Open Definition..."),(IOpImpID,"Open Implementation..."),
				(IFindNID,"Find Again"),
				(IUndoID,"Undo"),
				(IFindNID, "Find Again"),(IFindDID,"Find Definition"),(IFindIID,"Find Implementation"),
				(IExecuID,"Run"),
				(IShowCID,"Show Clipboard") ]
		THEN ChangeMenuItemFunctions
			[	(IOpDefID,OpenDefOrImp True False),(IOpImpID,OpenDefOrImp False False)];

/* Aux. function: Enables/disables the Edit menu items */

AbleEditItems :: !WdType !UndoMenuItem !Bool !Clipboard !IO -> IO;
AbleEditItems wdtype menu=:{undo,action} has_sel clip io
	= io	THEN EnableMenuItems enable`
			THEN DisableMenuItems disable`
			THEN ChangeMenuItemTitles titles`;
	where {
	enable` | has_undo		= [IUndoID : enable5];
							= enable5;
	enable5 | info_win		= [IFormaID : enable4];
							= enable4;
	enable4	| editable		= [IBalanID,ISelecID : enable3];
							= enable3;
	enable3 | has_cut		= [ICutID,IClearID : enable2];
							= enable2;
	enable2 | has_copy		= [ICopyID, IShiftLeftID, IShiftRightID : enable1];
							= enable1;
	enable1 | has_paste		= [IPasteID];
							= [];
	disable` | has_undo		= disable5;
							= [IUndoID : disable5];
	disable5 | info_win		= disable4;
							= [IFormaID : disable4];
	disable4 | editable		= disable3;
							= [IBalanID,ISelecID : disable3];
	disable3 | has_cut		= disable2;
							= [ICutID,IClearID : disable2];
	disable2 | has_copy		= disable1;
							= [ICopyID, IShiftLeftID, IShiftRightID : disable1];
	disable1 | has_paste	= [];
							= [IPasteID];
	titles`	| not has_undo	= [(IUndoID, "Undo")];
			| undo			= [(IUndoID, "Undo" +++action)];
							= [(IUndoID, "Redo" +++ action)];
	has_paste				= not (IsEmptyList clip) && modifiable;
	has_cut					= modifiable && has_sel;
	info_win				= editable || wdtype == ClpbrdWd || wdtype == ProjectWd;
	has_copy				= editable && has_sel;
	has_undo				= menu.action <> "" && editable;
	modifiable				= wdtype == EditWd;
	editable				= modifiable || wdtype == TypeWd || wdtype == ErrorWd;
	};

/* Aux. function: Enables/disables the File menu items */

AbleFileItems :: !WdType !Bool !Bool !Bool !String !IO -> IO;
AbleFileItems wdtype dcl_icl saved has_sel sel_text io
	= io	THEN EnableMenuItems enable`
			THEN DisableMenuItems disable`
			THEN ChangeMenuItemTitles title`
			THEN ChangeMenuItemFunctions menuFunctions;
	where {
	enable` | saved		= enable4;
						= [ISaveID,IReverID : enable4];
// MW...
 enable4 | wdtype <> ProjectWd = IfUnixSystem enable3 [IPrintID : enable3]
      = enable3;
// ... MW
	enable3	| dcl_icl	= [ISwapID : enable2];
						= enable2;
	enable2 | not_clip	= [ISavesID];
						= [];
	disable` | saved	= [ISaveID,IReverID : disable4];
						= disable4;
// MW...
 disable4 | wdtype <> ProjectWd = disable3
      = IfUnixSystem disable3 [IPrintID : disable3];
// ... MW
	disable3 | dcl_icl	= disable2;
						= [ISwapID : disable2];
	disable2 | not_clip	= [];
						= [ISavesID : []];

	title` | has_sel`	= [	(IOpDefID,"Open '" +++ MakeDefPathname sel_text +++ "'"),
							(IOpImpID,"Open '" +++ MakeImpPathname sel_text +++ "'") ];
						= [	(IOpDefID,"Open Definition..."),
							(IOpImpID,"Open Implementation...") ];
	has_sel`			= has_sel && CleanModId sel_text;
	not_clip			= wdtype <> ClpbrdWd && wdtype <> ProjectWd;
	menuFunctions
		= [	(IOpDefID,OpenDefOrImp True has_sel`), (IOpImpID,OpenDefOrImp False has_sel`) ];
	};
	
/* Aux. function: Enables/disables the Search menu items */

AbleFindItems :: !WdType !Bool !Bool !String !FindInfo !IO -> IO;
AbleFindItems wdtype error has_sel sel_text finfo io
	= io	THEN EnableMenuItems enable`
			THEN DisableMenuItems disable`
			THEN ChangeMenuItemTitles titles`;
	where {
	enable` | has_find	= [IFindNID : enable5];
						= enable5;
	enable5 | editable	= [IFindID,IGotoCID,IGotoLID,IFindIdID : enable4];
						= enable4;
	enable4 | has_sel`	= [IFindDID,IFindIID : enable3];
						= enable3;
	enable3 | error		= [IFindEID : enable2];
						= enable2;
	enable2 | has_sel	= [IFindSID : enable1];
						= enable1;
	enable1 | has_repl	= [IReplaID];
						= [];
	disable` | has_find	= disable5;
						= [IFindNID : disable5];
	disable5 | editable	= disable4;
						= [IFindID,IGotoCID,IGotoLID,IFindIdID : disable4];
	disable4 | has_sel`	= disable3;
						= [IFindDID,IFindIID : disable3];
	disable3 | error	= disable2;
						= [IFindEID : disable2];
	disable2 | has_sel	= disable1;
						= [IFindSID : disable1];
	disable1 | has_repl	= [];
						= [IReplaID];
	titles`
		| not has_find	= [ (IFindNID, "Find Again") : titles1 ];
		| finfo.backw	= [ (IFindNID, "Find Previous") : titles1 ];
						= [ (IFindNID, "Find Next") : titles1 ];
	titles1 | has_sel`	= [ (IFindDID, "Find Definition '" +++ sel_text +++ "'"),
							(IFindIID, "Find Implementation '" +++ sel_text +++ "'") ];
						= [ (IFindDID, "Find Definition"),
							(IFindIID, "Find Implementation") ];
	modifiable			= wdtype == EditWd;
	editable			= modifiable || wdtype == TypeWd || wdtype == ErrorWd;
	match				= StringMatch 0 (size finfo.find) finfo.find sel_text finfo.ignore False;
	has_repl			= has_find && modifiable && match && finfo.replace <> "";
	has_find			= finfo.find <> "" && editable;
	has_sel`			= has_sel && CleanModId sel_text;
	};
	
/* Aux. function: Enables/disables the Project menu items */
	
AbleProjectItems :: !WdType !Bool !Bool !IO -> IO;
AbleProjectItems wdtype prj_set is_icl_mod io
	= io	THEN EnableMenuItems enable`
			THEN DisableMenuItems disable`;
	where {
	enable`	| not_set		= [INewPrID : enable2];
							= enable2;
	enable2 | prj_set		= [ICloPrID, ILinkOptionsID : enable1];
							= [IOpePrID : enable1];
	enable1 | set			= [ISetPrID];
							= [];
	disable` | not_set		= disable2;
							= [INewPrID : disable2];
	disable2 | prj_set		= [IOpePrID : disable1];
							= [ICloPrID : disable1];
	disable1 | set			= [];
							= [ISetPrID];
	set						= prj_set && is_icl_mod;
	not_set					= not prj_set && is_icl_mod;
	};
	
/* Aux. function: Enables/disables the Commands menu items */
	
AbleCommandItems :: !WdType !Bool !Bool !Bool !Pathname !IO -> IO;
AbleCommandItems wdtype project_set main_mod_set exec_in_front path io
	= io	THEN EnableMenuItems enable`
			THEN DisableMenuItems disable`
			THEN ChangeMenuItemTitles titles`;
	where {
	enable` | exec_in_front	= [
								ICompiID,ICheckID,IGenerID : enable1];
							= enable1;
	enable1 | exec			= [IExecuID,IBriUDID];
							= [];
	disable` | exec_in_front= disable1;
							= [
								ICompiID,ICheckID,IGenerID : disable1];
	disable1 | exec			= [];
							= [IBriUDID,IExecuID];
	titles` | exec 			= [(IExecuID,"Run '" +++ path +++ "'")];
							= [(IExecuID,"Run")];	
	exec					= project_set || main_mod_set;  
	};
	
/* Aux. function: Enables/disables the Windows menu items */
		
AbleWindowItems :: !WdType !EditWdId !EditWdId !IO -> IO;
AbleWindowItems wd oldid newid io
	| oldid == newid	= io;
						= io`;
	where {
	io` | new_clip	= io1;
					= MarkMenuItems [newid] io1;
	io1 | old_clip	= io;
					= UnmarkMenuItems [oldid] io;
	new_clip		= newid == NoWdID || newid == ClpbrdWdID || newid == ProjectWdID;
	old_clip		= oldid == NoWdID || oldid == ClpbrdWdID || oldid == ProjectWdID;
	};
	
/* Aux. function: Enables/disables the Timer (cursor flashing when there is no selection in the
   front window */
	
AbleTimer :: !WdType !Bool !IO -> IO;
AbleTimer wdtype has_select io
	| has_select || no_edit	= DisableTimer TimerID io;
							= EnableTimer TimerID io;
	where {
	no_edit = wdtype == ClpbrdWd || wdtype == ProjectWd;
	};

/* Aux. function: returns selected text from the current line. If there is no selection or more
   than one line is selected the empty string is returned. */
	
FindSelection :: !Selection !CurLine -> (!Bool, !String);
FindSelection {tsel=sel=:{l1,c1,l2,c2}} cline
	| (l1 == l2 && c1 == c2) || (l1 <> l2)
		= (False, "");
		= (True, Singleton clip);
	where {
	(clip,_,_)	= CutFromCurLine sel cline;
	};
