/* InteractionWindow.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    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., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "InteractionWindow.h"
#include "MainWindowStuff.h"
#include "WindowDispatcher.h"
#include "TextEdit.h"
#include "Memory.h"
#include "GlobalWindowMenuList.h"
#include "Main.h"
#include "GrowIcon.h"
#include "DataMunging.h"
#include "FindDialog.h"


#define HEIGHT (100)
#define INDENT (20)
#define BOTTOM (5)


struct InteractionWindowRec
	{
		MainWindowRec*			MainWindow;
		WinType*						ScreenID;
		GenericWindowRec*		MyGenericWindow; /* how the window event dispatcher knows us */
		MenuItemType*				MyMenuItem;
		TextEditRec*				Editor;
	};


/* create a new interaction window.  the caller is responsible for registering the */
/* interaction window with the main window. */
InteractionWindowRec*	NewInteractionWindow(struct MainWindowRec* MainWindow)
	{
		InteractionWindowRec*		Window;

		CheckPtrExistence(MainWindow);
		Window = (InteractionWindowRec*)AllocPtrCanFail(sizeof(InteractionWindowRec),
			"InteractionWindowRec");
		if (Window == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Window->MainWindow = MainWindow;
		Window->ScreenID = MakeNewWindow(eDocumentWindow,eWindowClosable,
			eWindowZoomable,eWindowResizable,INDENT,GetScreenHeight()
			- WindowOtherEdgeWidths(eDocumentWindow) - HEIGHT - BOTTOM,
			GetScreenWidth() - (2 + WindowOtherEdgeWidths(eDocumentWindow)) - 2 * INDENT,
			HEIGHT,(void (*)(void*))&InteractionWindowUpdator,Window);
		if (Window->ScreenID == 0)
			{
			 FailurePoint2:
				ReleasePtr((char*)Window);
				goto FailurePoint1;
			}
		SetWindowName(Window->ScreenID,"Output");
		Window->Editor = NewTextEdit(Window->ScreenID,
			(TEScrollType)(eTEVScrollBar | eTEHScrollBar),GetMonospacedFont(),9,
			-1,-1,GetWindowWidth(Window->ScreenID) + 2,GetWindowHeight(Window->ScreenID) + 2);
		if (Window->Editor == NIL)
			{
			 FailurePoint3:
				KillWindow(Window->ScreenID);
				goto FailurePoint2;
			}
		Window->MyGenericWindow = CheckInNewWindow(Window->ScreenID,Window,
			(void (*)(void*,MyBoolean,OrdType,OrdType,ModifierFlags))&InteractionWindowDoIdle,
			(void (*)(void*))&InteractionWindowBecomeActive,
			(void (*)(void*))&InteractionWindowBecomeInactive,
			(void (*)(void*))&InteractionWindowJustResized,
			(void (*)(OrdType,OrdType,ModifierFlags,void*))&InteractionWindowDoMouseDown,
			(void (*)(unsigned char,ModifierFlags,void*))&InteractionWindowDoKeyDown,
			(void (*)(void*))&InteractionWindowClose,
			(void (*)(void*))&InteractionWindowMenuSetup,
			(void (*)(void*,MenuItemType*))&InteractionWindowDoMenuCommand);
		if (Window->MyGenericWindow == NIL)
			{
			 FailurePoint4:
				DisposeTextEdit(Window->Editor);
				goto FailurePoint3;
			}
		Window->MyMenuItem = MakeNewMenuItem(mmWindowMenu,"Output",0);
		if (Window->MyMenuItem == NIL)
			{
			 FailurePoint5:
				CheckOutDyingWindow(Window->MyGenericWindow);
				goto FailurePoint4;
			}
		if (!RegisterWindowMenuItem(Window->MyMenuItem,(void (*)(void*))&ActivateThisWindow,
			Window->ScreenID))
			{
			 FailurePoint6:
				KillMenuItem(Window->MyMenuItem);
				goto FailurePoint5;
			}
		SetTextEditAutoIndent(Window->Editor,True);
		SetTextEditTabSize(Window->Editor,MainWindowGetTabSize(MainWindow));
		return Window;
	}


/* dispose of a interaction window.  the interaction window notifies the main window */
/* that owns it. */
void									DisposeInteractionWindow(InteractionWindowRec* Window)
	{
		CheckPtrExistence(Window);
		MainWindowInteractionClosingNotify(Window->MainWindow,Window);
		DeregisterWindowMenuItem(Window->MyMenuItem);
		KillMenuItem(Window->MyMenuItem);
		CheckOutDyingWindow(Window->MyGenericWindow);
		DisposeTextEdit(Window->Editor);
		KillWindow(Window->ScreenID);
		ReleasePtr((char*)Window);
	}


void									InteractionWindowDoIdle(InteractionWindowRec* Window,
												MyBoolean CheckCursorFlag, OrdType XLoc, OrdType YLoc,
												ModifierFlags Modifiers)
	{
		CheckPtrExistence(Window);
		TextEditUpdateCursor(Window->Editor);
		if (CheckCursorFlag)
			{
				if (TextEditIBeamTest(Window->Editor,XLoc,YLoc))
					{
						SetIBeamCursor();
					}
				 else
					{
						SetArrowCursor();
					}
			}
	}


void									InteractionWindowBecomeActive(InteractionWindowRec* Window)
	{
		OrdType						XSize;
		OrdType						YSize;

		CheckPtrExistence(Window);
		EnableTextEditSelection(Window->Editor);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,XSize - 15,YSize - 15,XSize,YSize);
		DrawBitmap(Window->ScreenID,XSize-15,YSize-15,GetGrowIcon(True/*enablegrowicon*/));
	}


void									InteractionWindowBecomeInactive(InteractionWindowRec* Window)
	{
		OrdType						XSize;
		OrdType						YSize;

		CheckPtrExistence(Window);
		DisableTextEditSelection(Window->Editor);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,XSize - 15,YSize - 15,XSize,YSize);
		DrawBitmap(Window->ScreenID,XSize-15,YSize-15,GetGrowIcon(False/*disablegrowicon*/));
	}


void									InteractionWindowJustResized(InteractionWindowRec* Window)
	{
		OrdType						XSize;
		OrdType						YSize;

		CheckPtrExistence(Window);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,0,0,XSize,YSize);
		DrawBoxErase(Window->ScreenID,0,0,XSize,YSize);
		SetTextEditPosition(Window->Editor,-1,-1,XSize + 2,YSize + 2);
	}


void									InteractionWindowDoMouseDown(OrdType XLoc, OrdType YLoc,
												ModifierFlags Modifiers, InteractionWindowRec* Window)
	{
		CheckPtrExistence(Window);
		if ((XLoc >= GetWindowWidth(Window->ScreenID) - 15)
			&& (XLoc <= GetWindowWidth(Window->ScreenID))
			&& (YLoc >= GetWindowHeight(Window->ScreenID) - 15)
			&& (YLoc <= GetWindowHeight(Window->ScreenID)))
			{
				UserGrowWindow(Window->ScreenID,XLoc,YLoc);
				InteractionWindowJustResized(Window);
			}
		else if (TextEditHitTest(Window->Editor,XLoc,YLoc))
			{
				TextEditDoMouseDown(Window->Editor,XLoc,YLoc,Modifiers);
			}
	}


void									InteractionWindowDoKeyDown(unsigned char KeyCode,
												ModifierFlags Modifiers, InteractionWindowRec* Window)
	{
		CheckPtrExistence(Window);
		TextEditDoKeyPressed(Window->Editor,KeyCode,Modifiers);
	}


void									InteractionWindowClose(InteractionWindowRec* Window)
	{
		CheckPtrExistence(Window);
		DisposeInteractionWindow(Window);
	}


void									InteractionWindowUpdator(InteractionWindowRec* Window)
	{
		OrdType			XSize;
		OrdType			YSize;

		CheckPtrExistence(Window);
		TextEditFullRedraw(Window->Editor);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,XSize - 15,YSize - 15,XSize,YSize);
		DrawBitmap(Window->ScreenID,XSize-15,YSize-15,
			GetGrowIcon(Window->MyGenericWindow == GetCurrentWindowID()));
	}


void									InteractionWindowMenuSetup(InteractionWindowRec* Window)
	{
		CheckPtrExistence(Window);
		MainWindowEnableGlobalMenus(Window->MainWindow);
		EnableMenuItem(mPaste);
		ChangeItemName(mPaste,"Paste Text");
		if (TextEditIsThereValidSelection(Window->Editor))
			{
				EnableMenuItem(mCut);
				ChangeItemName(mCut,"Cut Text");
				EnableMenuItem(mCopy);
				ChangeItemName(mCopy,"Copy Text");
				EnableMenuItem(mClear);
				ChangeItemName(mClear,"Clear Text");
			}
		EnableMenuItem(mShiftLeft);
		EnableMenuItem(mShiftRight);
		EnableMenuItem(mBalanceParens);
		EnableMenuItem(mSelectAll);
		ChangeItemName(mSelectAll,"Select All Text");
		if (TextEditCanWeUndo(Window->Editor))
			{
				EnableMenuItem(mUndo);
				ChangeItemName(mUndo,"Undo Text Change");
			}
		ChangeItemName(mCloseFile,"Close Interaction");
		EnableMenuItem(mCloseFile);
		EnableMenuItem(mFind);
		if (PtrSize(GlobalSearchString) != 0)
			{
				EnableMenuItem(mFindAgain);
				if (TextEditIsThereValidSelection(Window->Editor))
					{
						EnableMenuItem(mReplace);
						EnableMenuItem(mReplaceAndFindAgain);
					}
			}
		EnableMenuItem(mShowSelection);
		if (TextEditIsThereValidSelection(Window->Editor))
			{
				EnableMenuItem(mEnterSelection);
			}
		SetItemCheckmark(Window->MyMenuItem);
	}


void									InteractionWindowDoMenuCommand(InteractionWindowRec* Window,
												MenuItemType* MenuItem)
	{
		CheckPtrExistence(Window);
		if (MainWindowDoGlobalMenuItem(Window->MainWindow,MenuItem))
			{
			}
		else if (MenuItem == mPaste)
			{
				TextEditDoMenuPaste(Window->Editor);
			}
		else if (MenuItem == mCut)
			{
				TextEditDoMenuCut(Window->Editor);
			}
		else if (MenuItem == mCopy)
			{
				TextEditDoMenuCopy(Window->Editor);
			}
		else if (MenuItem == mClear)
			{
				TextEditDoMenuClear(Window->Editor);
			}
		else if (MenuItem == mSelectAll)
			{
				TextEditDoMenuSelectAll(Window->Editor);
			}
		else if (MenuItem == mUndo)
			{
				TextEditDoMenuUndo(Window->Editor);
			}
		else if (MenuItem == mCloseFile)
			{
				InteractionWindowClose(Window);
			}
		else if (MenuItem == mShiftLeft)
			{
				TextEditShiftSelectionLeftOneTab(Window->Editor);
			}
		else if (MenuItem == mShiftRight)
			{
				TextEditShiftSelectionRightOneTab(Window->Editor);
			}
		else if (MenuItem == mBalanceParens)
			{
				TextEditBalanceParens(Window->Editor);
			}
		else if (MenuItem == mFind)
			{
				switch (DoFindDialog(&GlobalSearchString,&GlobalReplaceString,
					mCut,mPaste,mCopy,mUndo,mSelectAll,mClear))
					{
						default:
							EXECUTE(PRERR(ForceAbort,
								"InteractionWindowDoMenuCommand:  bad value from DoFindDialog"));
							break;
						case eFindCancel:
						case eDontFind:
							break;
						case eFindFromStart:
							SetTextEditInsertionPoint(Window->Editor,0,0);
							TextEditFindAgain(Window->Editor,GlobalSearchString);
							TextEditShowSelection(Window->Editor);
							break;
						case eFindAgain:
							TextEditFindAgain(Window->Editor,GlobalSearchString);
							TextEditShowSelection(Window->Editor);
							break;
					}
			}
		else if (MenuItem == mFindAgain)
			{
				TextEditFindAgain(Window->Editor,GlobalSearchString);
				TextEditShowSelection(Window->Editor);
			}
		else if (MenuItem == mReplace)
			{
				if (TextEditIsThereValidSelection(Window->Editor))
					{
						TextEditInsertRawDataWithUndo(Window->Editor,GlobalReplaceString,
							SYSTEMLINEFEED);
					}
			}
		else if (MenuItem == mReplaceAndFindAgain)
			{
				if (TextEditIsThereValidSelection(Window->Editor))
					{
						TextEditInsertRawDataWithUndo(Window->Editor,GlobalReplaceString,
							SYSTEMLINEFEED);
						TextEditFindAgain(Window->Editor,GlobalSearchString);
						TextEditShowSelection(Window->Editor);
					}
			}
		else if (MenuItem == mShowSelection)
			{
				TextEditShowSelection(Window->Editor);
			}
		else if (MenuItem == mEnterSelection)
			{
				char*						NewString;

				NewString = TextEditGetSelection(Window->Editor);
				if (NewString != NIL)
					{
						ReleasePtr(GlobalSearchString);
						GlobalSearchString = NewString;
					}
			}
		else
			{
				EXECUTE(PRERR(AllowResume,"InteractionWindowDoMenuCommand:  unknown menu command"));
			}
	}


/* add a string to the interaction window.  linefeeds correspond to system linefeed */
/* strings.  the string is a non-null-terminated block of data with explicit size. */
MyBoolean							InteractionWindowAppendString(InteractionWindowRec* Window,
												char* Data, long Length)
	{
		char*								Block;
		MyBoolean						Success;

		CheckPtrExistence(Window);
		Block = GetTextEditLine(Window->Editor,GetTextEditNumLines(Window->Editor) - 1);
		if (Block == NIL)
			{
				return False;
			}
		SetTextEditInsertionPoint(Window->Editor,GetTextEditNumLines(Window->Editor) - 1,
			PtrSize(Block));
		ReleasePtr(Block);
		Block = BlockFromRaw(Data,Length);
		if (Block == NIL)
			{
				return False;
			}
		Success = TextEditInsertRawData(Window->Editor,Block,SYSTEMLINEFEED);
		ReleasePtr(Block);
		TextEditShowSelection(Window->Editor);
		return Success;
	}
