const CString TUT_gs_csPause = ";";

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vStartRecord (HMODULE _hModule, DWORD _dThreadID)
{
	m_oListOfCommands . RemoveAll ();
	m_fn_bStartStopHook (_hModule , _dThreadID , TRUE);
	m_xLastCommandTime = clock ();
	m_bInPause = TRUE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vStopRecord	(HMODULE _hModule, DWORD _dThreadID, LPCTSTR _szFileName)
{
	m_fn_bStartStopHook (_hModule , _dThreadID , FALSE);

	m_fn_vDeleteLastCommand ();

	if (_szFileName)
	{
		FILE *pFile = fopen (_szFileName , "wt");
		if (pFile)
		{
			while (m_oListOfCommands . GetCount ())
			{
				CString csCommand = m_oListOfCommands . RemoveHead ();
				/*if (csCommand != TUT_gs_csPause)*/		fprintf (pFile , "%s\n" , csCommand);
			}
			fclose (pFile);
		}
	}
	// remove list
	while (m_oListOfCommands . GetCount ())				m_oListOfCommands . RemoveHead ();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
#define TUT_M_vSetHookProc(hFunc,iHookType,szHookFuncName,hModule,hThreadID)\
if (!hFunc)\
{\
    if (!(hFunc = SetWindowsHookEx(iHookType,(HOOKPROC)GetProcAddress(hModule, szHookFuncName), hModule, hThreadID)))\
    {\
        return FALSE;\
    }\
}

#define TUT_M_vUnsetHookProc(hFunc)\
if (hFunc)\
{\
    UnhookWindowsHookEx(hFunc);\
    hFunc = NULL;\
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bStartStopHook (HMODULE _hModule, DWORD _dThreadID, BOOL _bStart)
{
    static HHOOK hhkGetMessage     = NULL;
    static HHOOK hhkCallWndProc    = NULL;
    static HHOOK hhkCallWndProcRet = NULL;

	if (_bStart)
	{
		TUT_M_vSetHookProc(hhkGetMessage,		WH_GETMESSAGE,		"SpyGetMsgProc",	_hModule,_dThreadID);
		TUT_M_vSetHookProc(hhkCallWndProc,		WH_CALLWNDPROC,		"SpyGetWndProc",	_hModule,_dThreadID);
		TUT_M_vSetHookProc(hhkCallWndProcRet,	WH_CALLWNDPROCRET,	"SpyGetWndProcRet",	_hModule,_dThreadID);
	}
	else
	{
		TUT_M_vUnsetHookProc(hhkGetMessage);
		TUT_M_vUnsetHookProc(hhkCallWndProc);
		TUT_M_vUnsetHookProc(hhkCallWndProcRet);
	}
	return TRUE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
#define TUT_M_HandleMsg(iType,uiMess,Func)	case uiMess:	return Func(iType, hwnd , uiMessage , wParam , lParam);

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bHookMsgProc (HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	m_fn_vHandlePause ();

	switch (uiMessage)
	{
		TUT_M_HandleMsg(WH_GETMESSAGE,	WM_RBUTTONDOWN,		m_fn_bRButtonMsgProc)
		TUT_M_HandleMsg(WH_GETMESSAGE,	WM_LBUTTONUP,		m_fn_bLButtonMsgProc)
		TUT_M_HandleMsg(WH_GETMESSAGE,	WM_LBUTTONDOWN,		m_fn_bLButtonMsgProc)
		TUT_M_HandleMsg(WH_GETMESSAGE,	WM_LBUTTONDBLCLK,	m_fn_bLButtonMsgProc)
		TUT_M_HandleMsg(WH_GETMESSAGE,	WM_MOUSEMOVE,		m_fn_bMouseMsgProc)
		
	default : break;
	}
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bHookWndProc (HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	m_fn_vHandlePause ();

	switch (uiMessage)
	{
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_COMMAND,			m_fn_bCommandMsgProc)
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_KILLFOCUS,		m_fn_bFocusMsgProc)
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_NOTIFY,			m_fn_bNotifyMsgProc)
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_INITMENU,		m_fn_bMenuMsgProc)
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_INITMENUPOPUP,	m_fn_bMenuMsgProc)
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_MENUSELECT,		m_fn_bMenuMsgProc)
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_EXITMENULOOP,	m_fn_bMenuMsgProc)
		TUT_M_HandleMsg(WH_CALLWNDPROC,	WM_MOUSEMOVE,		m_fn_bMouseMsgProc)
	default : break;
	}
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bHookWndProcRet (HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	m_fn_vHandlePause ();

	switch (uiMessage)
	{
	default : break;
	}
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vHandlePause ()
{
	clock_t xDuration = clock () - m_xLastCommandTime;

	if (!m_bInPause && xDuration > TUT_C_xPauseIntervall)
	{
		xDuration = TUT_C_xPauseIntervall;
		m_fn_vAddPause ();
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vAddCommand (CString csCommand)
{
	m_oListOfCommands . AddTail (csCommand);
	m_bInPause = FALSE;
	m_xLastCommandTime = clock ();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vInsertAfterPause (CString csCommand)
{
	POSITION xPos;

	xPos = m_oListOfCommands . GetTailPosition ();

	while (xPos && m_oListOfCommands . GetAt (xPos) != TUT_gs_csPause)
	{
		m_oListOfCommands . GetPrev (xPos);
	}

	if (xPos)
	{
		m_oListOfCommands . InsertBefore (xPos , csCommand);
	}
	else
	{
		m_oListOfCommands . AddTail (csCommand);
	}
	m_bInPause = FALSE;
	m_xLastCommandTime = clock ();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vAddPause (void)
{
	m_oListOfCommands . AddTail (TUT_gs_csPause);
	m_bInPause = TRUE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vRemoveToPause (void)
{
	while (!m_oListOfCommands . IsEmpty () && m_oListOfCommands . GetTail () != TUT_gs_csPause)
	{
		m_oListOfCommands . RemoveTail ();
	}

	m_bInPause = TRUE;
	m_xLastCommandTime = clock ();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bHaveSomethingAfterPause (void)
{
	return (m_oListOfCommands . GetCount () && m_oListOfCommands . GetTail () != TUT_gs_csPause);
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vDeleteLastCommand (void)
{
	// delete last Pause commands
	while (!m_oListOfCommands . IsEmpty () && m_oListOfCommands . GetTail () == TUT_gs_csPause)
	{
		m_oListOfCommands . RemoveTail ();
	}
	
	// delete last command
	m_fn_vRemoveToPause ();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
#define TUT_M_bWND_VALID_AND_USABLE(p_stWndDefinition)\
	( (p_stWndDefinition) && (p_stWndDefinition) -> hWnd && IsWindowVisible((p_stWndDefinition) -> hWnd) )

#define TUT_M_ASSERT_WND_VALID_AND_USABLE(p_stWndDefinition)\
	if (!TUT_M_bWND_VALID_AND_USABLE(p_stWndDefinition))	return FALSE;

#define TUT_M_ASSERT_CURSOR_UNDER_CONTROL(hwnd)\
{\
	RECT stRect;\
	POINT xPoint;\
	GetWindowRect (hwnd , &stRect);\
	GetCursorPos (&xPoint);\
	if (!PtInRect (&stRect , xPoint))		return FALSE;\
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bCommandMsgProc (int /*iHookType*/, HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	TUT_tdstWndDefinition	*p_stWndDefinition	= lParam ? m_fnp_stFindWndDefinition ((HWND)lParam) : NULL;
	UINT					uiNotification		= HIWORD(wParam);
	CString					csText;

	TUT_M_ASSERT_WND_VALID_AND_USABLE(p_stWndDefinition);

	switch (p_stWndDefinition -> eType)
	{
	case TUT_eButton:
		{
			if (uiNotification == BN_CLICKED)
			{
				csText . Format ("%s(\"%s\")" , TUT_C_szClic , p_stWndDefinition -> csName);
				m_fn_vAddCommand (csText);
				return TRUE;
			}
		}
		break;

	case TUT_eListBox:
		{
			switch (uiNotification)
			{
			case LBN_SELCHANGE:
				{
					long lType = GetWindowLong (p_stWndDefinition -> hWnd, GWL_STYLE);
					if (! (lType & LBS_MULTIPLESEL) )
					{					
						int iCurSel = SendMessage (p_stWndDefinition -> hWnd, LB_GETCURSEL, 0, 0);
						if (iCurSel != LB_ERR)
						{
							char szText[255];

							SendMessage (p_stWndDefinition -> hWnd, LB_GETTEXT, iCurSel, (DWORD)szText);
							csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szSelect , p_stWndDefinition -> csName , szText);
							m_fn_vAddCommand (csText);
							return TRUE;
						}
					}
				}
				break;
			case LBN_DBLCLK:
				{
					if (m_oListOfCommands . GetCount () > 0)
					{
						csText . Format ("%s()" , TUT_C_szDoubleClic);
						m_fn_vInsertAfterPause (csText);
						return TRUE;
					}
				}
				break;
			} // switch (uiNotification)
		}
		break;

	case TUT_eComboBox:
		{
			switch (uiNotification)
			{
				case CBN_SELCHANGE:
				{
					int iCurSel = SendMessage (p_stWndDefinition -> hWnd, CB_GETCURSEL, 0, 0);
					if (iCurSel != CB_ERR)
					{
						char szText[255];

						SendMessage (p_stWndDefinition -> hWnd, CB_GETLBTEXT, iCurSel, (DWORD)szText);
						csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szSelect , p_stWndDefinition -> csName , szText);
						m_fn_vAddCommand (csText);
						return TRUE;
					}
				}
				break;
			case CBN_DBLCLK:
				{
					if (m_oListOfCommands . GetCount () > 0)
					{
						csText . Format ("%s()" , TUT_C_szDoubleClic);
						m_fn_vInsertAfterPause (csText);
						return TRUE;
					}
				}
				break;
			} // switch (uiNotification)
		}
		break;
	} // switch (p_stWndDefinition -> eType)
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
CString gs_fn_csHandleQuoteInString (char *_szSourceText)
{
	CString csResult;
	char *p_cBegin = _szSourceText;

	csResult . Empty ();

	while (*p_cBegin)
	{
		if (*p_cBegin == '\"')
		{
			csResult += "''";
		}
		else
		{
			csResult += *p_cBegin;
		}
		p_cBegin ++;
	}
	return csResult;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bFocusMsgProc (int /*iHookType*/, HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	if (uiMessage == WM_KILLFOCUS)
	{
		TUT_tdstWndDefinition	*p_stWndDefinition	= hwnd ? m_fnp_stFindWndDefinition (hwnd) : NULL;
		CString					csText;

		TUT_M_ASSERT_WND_VALID_AND_USABLE(p_stWndDefinition);

		switch (p_stWndDefinition -> eType)
		{
		case TUT_eListBox:
			{
				long lType = GetWindowLong (p_stWndDefinition -> hWnd , GWL_STYLE);
				if (lType & LBS_MULTIPLESEL)
				{
					int iNbSel = SendMessage (p_stWndDefinition -> hWnd , LB_GETSELCOUNT , 0 , 0);
					if (iNbSel != LB_ERR)
					{
						int *a_iSelItems = (int*) alloca (iNbSel * sizeof (int));

						if (SendMessage (p_stWndDefinition -> hWnd , LB_GETSELITEMS , iNbSel , (DWORD)a_iSelItems))
						{
							char szText[255];

							SendMessage (p_stWndDefinition -> hWnd , LB_GETTEXT , a_iSelItems [0] , (DWORD)szText);
							csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szSelect , p_stWndDefinition -> csName , szText);
							m_fn_vAddCommand (csText);

							for (int i=1 ; i<iNbSel ; i++)
							{
								SendMessage (p_stWndDefinition -> hWnd , LB_GETTEXT , a_iSelItems [i] , (DWORD)szText);
								csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szAddSelect , p_stWndDefinition -> csName , szText);
								m_fn_vAddCommand (csText);
							}
							return TRUE;
						}
					}
				}
			}
			break;

		case TUT_eTextEdit:
			{
				long lType = GetWindowLong (p_stWndDefinition -> hWnd , GWL_STYLE);
				if (lType & ES_MULTILINE)
				{
					int	iSize				= GetWindowTextLength (p_stWndDefinition -> hWnd);
					char *szText			= (char *) alloca (iSize+1);
					char szEndOfLine [3]	= { 13 , 10 , 0 };
					char *p_cBegin , *p_cEnd;
					int iLineNumber;

					// clear control
					csText . Format ("%s(0,0,0,0)" , TUT_C_szSelectText);
					m_fn_vAddCommand (csText);

					GetWindowText (p_stWndDefinition -> hWnd , szText , iSize + 1);
					p_cBegin = szText;

					iLineNumber = 0;
					p_cEnd = strstr (p_cBegin , szEndOfLine);					
					while (p_cEnd)
					{
						*p_cEnd = 0;
						if (p_cEnd != p_cBegin)
						{
							csText . Format ("%s(\"%s\" , \"%s\" , %d)" , TUT_C_szSetEditText , p_stWndDefinition -> csName , gs_fn_csHandleQuoteInString (p_cBegin) , iLineNumber);
							m_fn_vAddCommand (csText);
						}

						//
						p_cBegin = p_cEnd + 2;
						iLineNumber ++;
						p_cEnd = strstr (p_cBegin , szEndOfLine);					
					}

					if (*p_cBegin)
					{
						csText . Format ("%s(\"%s\" , \"%s\" , %d)" , TUT_C_szSetEditText , p_stWndDefinition -> csName , gs_fn_csHandleQuoteInString (p_cBegin) , iLineNumber);
						m_fn_vAddCommand (csText);
					}
					return TRUE;
				}
				else
				{
					int	iSize		= GetWindowTextLength (p_stWndDefinition -> hWnd);
					char *szText	= (char *) alloca (iSize+1);

					if (GetWindowText (p_stWndDefinition -> hWnd , szText , iSize+1))
					{
						csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szSetEditText , p_stWndDefinition -> csName , gs_fn_csHandleQuoteInString (szText));
						m_fn_vAddCommand (csText);
						return TRUE;
					}
				}
			}
			break;
		} // switch (p_stWndDefinition -> eType)
	}

	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bNotifyMsgProc (int iHookType, HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	LPNMHDR pnmh = (LPNMHDR) lParam;
	TUT_tdstWndDefinition	*p_stWndDefinition	= (pnmh && pnmh -> hwndFrom) ? m_fnp_stFindWndDefinition (pnmh -> hwndFrom) : NULL;
	CString csText;
	
	TUT_M_ASSERT_WND_VALID_AND_USABLE(p_stWndDefinition);
	TUT_M_ASSERT_CURSOR_UNDER_CONTROL(p_stWndDefinition -> hWnd);

	switch (p_stWndDefinition -> eType)
	{
	case TUT_eListBox:
		{
			uiMessage = uiMessage;
		}
		break;

	case TUT_eListCtrl:
		{
			NM_LISTVIEW *pnmv = (NM_LISTVIEW *) lParam;
			if (pnmv -> hdr . code == LVN_ITEMCHANGED)
			{
				if (pnmv -> iItem && pnmv -> uNewState == (LVIS_SELECTED|LVIS_FOCUSED))
				{
					char szText[255];

					ListView_GetItemText (p_stWndDefinition -> hWnd , pnmv -> iItem , 0 , szText , 255);
					csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szSelect , p_stWndDefinition -> csName , szText);
					m_fn_vAddCommand (csText);
					return TRUE;
				}
			}
		}
		break;

	case TUT_eTreeCtrl:
		{
			NM_TREEVIEW *pnmtv = (NM_TREEVIEW *) lParam;
			switch (pnmtv -> hdr . code)
			{
			case TVN_SELCHANGED:
				{
					if (pnmtv -> itemNew . mask & TVIF_STATE && pnmtv -> itemNew . state & TVIS_SELECTED)
					{
						char szText[255];

						pnmtv -> itemNew . pszText = szText;
						pnmtv -> itemNew . cchTextMax = 255;
						pnmtv -> itemNew . mask = TVIF_TEXT;

						if (TreeView_GetItem (p_stWndDefinition -> hWnd , & pnmtv -> itemNew))
						{
							csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szSelect , p_stWndDefinition -> csName , szText);
							m_fn_vAddCommand (csText);
							return TRUE;
						}
					}
				}
				break;
			case TVN_ITEMEXPANDED:
				{
					if (pnmtv -> itemNew . mask & TVIF_STATE)
					{
						BOOL bExpanded = (pnmtv -> itemNew . state & TVIS_EXPANDED) ? TRUE : FALSE;
						char szText[255];

						pnmtv -> itemNew . pszText = szText;
						pnmtv -> itemNew . cchTextMax = 255;
						pnmtv -> itemNew . mask = TVIF_TEXT;

						if (TreeView_GetItem (p_stWndDefinition -> hWnd , & pnmtv -> itemNew))
						{
							csText . Format ("%s(\"%s\" , \"%s\")" , bExpanded ? TUT_C_szExpand : TUT_C_szCollapse , p_stWndDefinition -> csName , szText);
							m_fn_vAddCommand (csText);
							return TRUE;
						}
					}
				}
				break;
			} // switch (pnmtv -> hdr . code)
		}
		break;
	} // switch (p_stWndDefinition -> eType)
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bLButtonMsgProc (int iHookType, HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	switch (iHookType)
	{
	case WH_CALLWNDPROCRET:
	case WH_GETMESSAGE:
		{
			switch (uiMessage)
			{
			case WM_LBUTTONDOWN:
				{
					TUT_tdstWndDefinition	*p_stWndDefinition	= hwnd ? m_fnp_stFindWndDefinition (hwnd) : NULL;

					if (p_stWndDefinition == NULL && m_fn_bIsModelView (hwnd))
					{
						return m_fn_bLButtonDownModelViewProc (iHookType, hwnd, uiMessage, wParam, lParam);
					}

					TUT_M_ASSERT_WND_VALID_AND_USABLE(p_stWndDefinition);

					m_csDragDropElement . Empty ();
					m_lDragDrop = 0;

					switch (p_stWndDefinition -> eType)
					{
					case TUT_e3DView:
						{
							return m_fn_bLButtonDown3DViewProc (iHookType, hwnd, uiMessage, wParam, lParam);
						}
						break;
					}					
				}
				break;
			case WM_LBUTTONUP:
				{
					TUT_tdstWndDefinition	*p_stWndDefinition	= hwnd ? m_fnp_stFindWndDefinition (hwnd) : NULL;

					if (p_stWndDefinition == NULL && m_fn_bIsModelView (hwnd))
					{
						return m_fn_bLButtonUpModelViewProc (iHookType, hwnd, uiMessage, wParam, lParam);
					}

					TUT_M_ASSERT_WND_VALID_AND_USABLE(p_stWndDefinition);
					switch (p_stWndDefinition -> eType)
					{
					case TUT_e3DView:
						{
							return m_fn_bLButtonUp3DViewProc (iHookType, hwnd, uiMessage, wParam, lParam);
						}
						break;
					}					
					m_csDragDropElement . Empty ();
					m_lDragDrop = 0;
				}
				break;
			case WM_LBUTTONDBLCLK:
				{
					if (m_fn_bHaveSomethingAfterPause ())
					{
						CString csText;

						csText . Format ("%s()" , TUT_C_szDoubleClic);
						m_fn_vInsertAfterPause (csText);
						return TRUE;
					}
				}
				break;
			} // switch (uiMessage)
		}
		break;
	} // switch (iHookType)
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bMouseMsgProc (int iHookType, HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	switch (uiMessage)
	{
	case WM_MOUSEMOVE:
		{
			if (m_lDragDrop > 0)	m_lDragDrop++;
		}
		break;
	}
	return FALSE;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bRButtonMsgProc (int iHookType, HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	switch (uiMessage)
	{
	case WM_RBUTTONDOWN:
		{
			TUT_tdstWndDefinition	*p_stWndDefinition	= hwnd ? m_fnp_stFindWndDefinition (hwnd) : NULL;
			CString					csText;

			TUT_M_ASSERT_WND_VALID_AND_USABLE(p_stWndDefinition);

			switch (p_stWndDefinition -> eType)
			{
			case TUT_e3DView:
				{
					csText . Format ("%s()" , TUT_C_szRightButton);
					m_fn_vAddCommand (csText);
					return TRUE;
				}
				break;
			case TUT_eListBox:
				{
					long lType = GetWindowLong (p_stWndDefinition -> hWnd , GWL_STYLE);
					if (lType & LBS_MULTIPLESEL)
					{
						int iNbSel = SendMessage (p_stWndDefinition -> hWnd , LB_GETSELCOUNT , 0 , 0);
						if (iNbSel != LB_ERR)
						{
							int *a_iSelItems = (int*) alloca (iNbSel * sizeof (int));

							if (SendMessage (p_stWndDefinition -> hWnd , LB_GETSELITEMS , iNbSel , (DWORD)a_iSelItems))
							{
								char szText[255];

								SendMessage (p_stWndDefinition -> hWnd , LB_GETTEXT ,  a_iSelItems [0] , (DWORD)szText);
								csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szSelect , p_stWndDefinition -> csName , szText);
								m_fn_vAddCommand (csText);

								for (int i=1 ; i<iNbSel ; i++)
								{
									SendMessage (p_stWndDefinition -> hWnd , LB_GETTEXT , a_iSelItems [i] , (DWORD)szText);
									csText . Format ("%s(\"%s\" , \"%s\")" , TUT_C_szAddSelect , p_stWndDefinition -> csName , szText);
									m_fn_vAddCommand (csText);
								}
							}
						}
					}
				}
				// no break here
			default:
				{
					csText . Format ("%s()" , TUT_C_szRightButton);
					m_fn_vAddCommand (csText);
					csText . Format ("%s(\"%s\")" , TUT_C_szClic , p_stWndDefinition -> csName);
					m_fn_vAddCommand (csText);
				}
				break;
			} // switch (p_stWndDefinition -> eType)

			//
		}
		break;
	} // switch (uiMessage)
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void gs_fn_vCleanMenuName(char *_szName)
{
	char *p_cSource = _szName;
	char *p_cTarget = _szName;

	while (*p_cSource)
	{
		if (*p_cSource == '\t')			break;
		if (*p_cSource != '&')
		{
			*p_cTarget++ = *p_cSource;
		}
		p_cSource++;
	}
	*p_cTarget = 0;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bMenuMsgProc (int /*iHookType*/, HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	static CStringList s_oListOfMenuNames;
	static CList<HMENU,HMENU> s_oListOfMenuHandles;
	static char s_oCurrentMenuName[255];
	static HMENU hCurrentMenu;
	static HMENU hNextMenu;
	static BOOL  bLastIsAnEntry;
	CString csText;

	switch (uiMessage)
	{
	case WM_INITMENU:
		{
			s_oListOfMenuNames . RemoveAll ();
			s_oListOfMenuHandles . RemoveAll ();
			*s_oCurrentMenuName = 0;
			hCurrentMenu = (HMENU)wParam;
			hNextMenu = NULL;
			bLastIsAnEntry = FALSE;
		}
		break;
	case WM_INITMENUPOPUP:
		{			
			hNextMenu = (HMENU) wParam;
		}
		break;
	case WM_MENUSELECT:
		{
			UINT uItem = (UINT) LOWORD(wParam);   // menu item or submenu index 
			UINT fuFlags = (UINT) HIWORD(wParam); // menu flags 
			HMENU hmenu = (HMENU) lParam;          // handle of menu clicked 
			UINT  uiFlag = (fuFlags & MF_POPUP) ? MF_BYPOSITION : MF_BYCOMMAND;

			csText = "WM_MENUSELECT : ";
			if (fuFlags == 0xFFFF)
			{
				if (!bLastIsAnEntry)
				{
					while (s_oListOfMenuNames . GetCount())			s_oListOfMenuNames . RemoveHead ();
					while (s_oListOfMenuHandles . GetCount())		s_oListOfMenuHandles . RemoveHead ();
					*s_oCurrentMenuName = 0;
					csText += "Clear all";
				}
				else
				{
					s_oListOfMenuNames . AddTail (s_oCurrentMenuName);
					csText += "End";
				}
			}
			else if (hmenu == hCurrentMenu)
			{
				GetMenuString (hmenu , uItem , s_oCurrentMenuName , 255 , uiFlag);
				gs_fn_vCleanMenuName (s_oCurrentMenuName);
				bLastIsAnEntry = (fuFlags & MF_POPUP) ? FALSE : TRUE;
				csText += "Current => " + CString(s_oCurrentMenuName);
			}
			else if (hmenu == hNextMenu)
			{
				s_oListOfMenuHandles . AddTail (hCurrentMenu);
				s_oListOfMenuNames . AddTail (s_oCurrentMenuName);
				GetMenuString (hmenu , uItem , s_oCurrentMenuName , 255 , uiFlag);
				gs_fn_vCleanMenuName (s_oCurrentMenuName);
				hCurrentMenu = hNextMenu;
				hNextMenu = NULL;
				bLastIsAnEntry = (fuFlags & MF_POPUP) ? FALSE : TRUE;
				csText += "Next    => " + CString(s_oCurrentMenuName);
			}
			else
			{
				if (s_oListOfMenuNames . GetCount())			s_oListOfMenuNames . RemoveTail ();
				while (s_oListOfMenuHandles . GetCount() && (s_oListOfMenuHandles . GetTail () != hmenu))
				{
					s_oListOfMenuHandles . RemoveTail ();
					if (s_oListOfMenuNames . GetCount())			s_oListOfMenuNames . RemoveTail ();
				}
				if (s_oListOfMenuHandles . GetCount())			s_oListOfMenuHandles . RemoveTail ();
				hCurrentMenu = hmenu;
				hNextMenu = NULL;
				GetMenuString (hmenu , uItem , s_oCurrentMenuName , 255 , uiFlag);
				gs_fn_vCleanMenuName (s_oCurrentMenuName);
				bLastIsAnEntry = (fuFlags & MF_POPUP) ? FALSE : TRUE;
				csText += "Back    => " + CString(s_oCurrentMenuName);
			}
		}
		break;
	case WM_EXITMENULOOP:
		{
			while (s_oListOfMenuHandles . GetCount())		s_oListOfMenuHandles . RemoveHead ();

			if (s_oListOfMenuNames . GetCount())
			{
				ASSERT(bLastIsAnEntry);
				csText . Format ("%s(\"%s\"" , wParam ? TUT_C_szPopupMenu : TUT_C_szMenu , s_oListOfMenuNames . RemoveHead ());
				while (s_oListOfMenuNames . GetCount())
				{
					csText = csText + " , \"" + s_oListOfMenuNames . RemoveHead () + "\"";
				}
				csText += ")";
				m_fn_vAddCommand (csText);
				return TRUE;
			}
		}
		break;
	}
	return FALSE;
}


