/******************************************************************************
*                                                                             *
* Tut_Mngr.cpp : definition of the TUT_CTutorialManager class                 *
*                                                                             *
* This class can take the control of all windows of the application           *
* to create a tutorial or an automatic test                                   *
*                                                                             *
* Author : Alexis Vaisse                                                      *
*                                                                             *
******************************************************************************/


#include "stdafx.h"
#include <afxrich.h>
#include <mmsystem.h>
#include "math.h"
#include <io.h>
#include <malloc.h>
//#include <stdio.h>
//#include <stdlib.h>
#include <time.h>

#include "Tut_Mngr.hpp"

//-----------------------------------------------------------------------------

TUT_CTutorialManager TUT_g_oTutorialManager;      // The one and only one instance of the TUT_CTutorialManager class
HANDLE               TUT_g_hMainThreadIdleEvent;  // Global event handle for communication between the two threads

typedef struct TUT_tdstKeyConversion_
{
    char * szKeyName;
    int    iVirtualKeyCode;
}
TUT_tdstKeyConversion;

static const TUT_tdstKeyConversion TUT_cgs_a_stKeyConversionTable [] =
{   {"Back"       , 0x08} ,
    {"Tab"        , 0x09} ,
    {"Return"     , 0x0D} ,
    {"Shift"      , 0x10} ,
    {"Control"    , 0x11} ,
    {"Alt"        , 0x12} ,
    {"Pause"      , 0x13} ,
    {"Caps"       , 0x14} ,
    {"Escape"     , 0x1B} ,
    {"Space"      , 0x20} ,

    {"PageUp"     , 0x21} ,
    {"PageDown"   , 0x22} ,
    {"End"        , 0x23} ,
    {"Home"       , 0x24} ,
    {"Left"       , 0x25} ,
    {"Up"         , 0x26} ,
    {"Right"      , 0x27} ,
    {"Down"       , 0x28} ,
    {"Ins"        , 0x2D} ,
    {"Del"        , 0x2E} ,


    {"0"          , 0x30} ,
    {"1"          , 0x31} ,
    {"2"          , 0x32} ,
    {"3"          , 0x33} ,
    {"4"          , 0x34} ,
    {"5"          , 0x35} ,
    {"6"          , 0x36} ,
    {"7"          , 0x37} ,
    {"8"          , 0x38} ,
    {"9"          , 0x39} ,

    {"Menu"       , 0x5D} ,

    {"Num0"       , 0x60} ,
    {"Num1"       , 0x61} ,
    {"Num2"       , 0x62} ,
    {"Num3"       , 0x63} ,
    {"Num4"       , 0x64} ,
    {"Num5"       , 0x65} ,
    {"Num6"       , 0x66} ,
    {"Num7"       , 0x67} ,
    {"Num8"       , 0x68} ,
    {"Num9"       , 0x69} ,
    {"Num*"       , 0x6A} ,
    {"Num+"       , 0x6B} ,
    {"Num-"       , 0x6D} ,
    {"Num."       , 0x6E} ,
    {"Num/"       , 0x6F} ,

    {"F1"         , 0x70} ,
    {"F2"         , 0x71} ,
    {"F3"         , 0x72} ,
    {"F4"         , 0x73} ,
    {"F5"         , 0x74} ,
    {"F6"         , 0x75} ,
    {"F7"         , 0x76} ,
    {"F8"         , 0x77} ,
    {"F9"         , 0x78} ,
    {"F10"        , 0x79} ,
    {"F11"        , 0x7A} ,
    {"F12"        , 0x7B} ,

    {"Num"        , 0x90} ,
    {"$"          , 0xBA} ,
    {"="          , 0xBB} ,
    {","          , 0xBC} ,
    {";"          , 0xBE} ,
    {":"          , 0xBF} ,
    {"ù"          , 0xC0} ,
    {")"          , 0xDB} ,
    {"*"          , 0xDC} ,
    {"^"          , 0xDD} ,
    {"²"          , 0xDE} ,
    {"!"          , 0xDF} ,
    {"<"          , 0xE2} ,

    {NULL         , 0x00}      // end of the list: DO NOT REMOVE !
};


//-----------------------------------------------------------------------------

#include "Tut_Mngr.inc"		// Low level member functions
#include "Tut_Mngr.cxx"		// Record script functions

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - TUT_gs_fn_vCallbackTimerFunction
// Callback timer function
//-----------------------------------------------------------------------------
void CALLBACK TUT_gs_fn_vCallbackTimerFunction (HWND _hWnd , UINT _uiMsg , UINT _uiIdEvent , DWORD dwTime)
{
    // If all messages of the main thread have been proceeded, signal the event for the Tutorial thread
    if (GetQueueStatus (QS_ALLEVENTS | QS_ALLINPUT) == 0)  PulseEvent (TUT_g_hMainThreadIdleEvent);


	if (TUT_g_oTutorialManager . m_fn_bNotInBackGround())					return;



    static HWND  s_hToolTipWindow        = NULL;   // The tool tip window
    static BOOL  s_hToolTipWindowVisible = FALSE;

    // The tool tip window is created once
    if (s_hToolTipWindow == NULL)
    {
        s_hToolTipWindow = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TOPMOST , "STATIC" , "" ,
                                           WS_BORDER | WS_POPUP , 0 , 0 , 1 , 1 , NULL , NULL , NULL , NULL);
    }

    // Get the window under the mouse cursor
    POINT stMousePos;
    GetCursorPos (& stMousePos);
    HWND  hWnd = WindowFromPoint (stMousePos);

    CString csStringToShow;
    BOOL    bVariableNameFound = FALSE;
    if (hWnd == TUT_g_oTutorialManager . m_fn_hGetDebugWindowHandle () && (GetAsyncKeyState (VK_SHIFT) & 0x8000))
    {
        HWND  hEditText   = hWnd;
        ASSERT (IsWindow (hEditText));

        POINT stClientPos = stMousePos;
        ScreenToClient (hEditText , & stClientPos);
        SendMessage (hEditText , WM_LBUTTONDOWN , MK_LBUTTON , MAKELPARAM (stClientPos . x , stClientPos . y));
        SendMessage (hEditText , WM_LBUTTONUP   , MK_LBUTTON , MAKELPARAM (stClientPos . x , stClientPos . y));

        // Get the index and the line of the pointed character
        CHARRANGE stCharRange;
        SendMessage (hEditText , EM_EXGETSEL , 0 , (LPARAM) & stCharRange);
        int   iCharNumber = stCharRange . cpMin;
        int   iLineIndex  = SendMessage (hEditText , EM_LINEFROMCHAR , iCharNumber , 0);
        int   iFirstChar  = SendMessage (hEditText , EM_LINEINDEX , iLineIndex , 0);

        // Get the content of the line
        char  szBuffer [256];
        TEXTRANGE stTextRange;
        stTextRange . chrg . cpMin = iFirstChar;
        stTextRange . chrg . cpMax = iFirstChar + 255;
        stTextRange . lpstrText    = szBuffer + 1;
        SendMessage (hEditText , EM_GETTEXTRANGE , 0 , (LPARAM) & stTextRange);
        szBuffer [0]   = 0;    // To be sure we will not go before
        szBuffer [255] = 0;    // To be sure we will not go after

        ASSERT (iCharNumber >= iFirstChar && iCharNumber < iFirstChar + 255);

        // Find the beginning of the variable name
        char * szBeginning = szBuffer + iCharNumber - iFirstChar;
        char   cChar  = * szBeginning;
        while (cChar != 0 && cChar != '"' && cChar != '(' && cChar != ')' && cChar != ',' && cChar != TUT_C_cBeginningOfVariable)
            cChar = * (--szBeginning);

        // Find the end of the variable name
        char * szEnd = szBuffer + iCharNumber - iFirstChar;
        cChar = * szEnd;
        while (cChar != 0 && cChar != '"' && cChar != '(' && cChar != ')' && cChar != ',' && cChar != TUT_C_cBeginningOfVariable)
            cChar = * (++szEnd);

        while (* (++ szBeginning) == ' ') ;    // skip the first character and all the spaces
        while (* (-- szEnd)       == ' ') ;    // skip all the spaces
        szEnd [1] = 0;

        TUT_tdstVariableDefinition * p_stVariableDef = TUT_g_oTutorialManager . m_fnp_stFindVariable (szBeginning);
        if (p_stVariableDef)
        {
            bVariableNameFound = TRUE;
            csStringToShow     = p_stVariableDef -> csValue;
        }
    }

    if (! bVariableNameFound)
    {
        // Check if the SHIFT and CONTROL keys are pressed
        if ((GetAsyncKeyState (VK_SHIFT) & 0x8000) == 0 || (GetAsyncKeyState (VK_CONTROL) & 0x8000) == 0)
        {
            if (s_hToolTipWindowVisible)
            {
                HWND hFocusWindow = GetFocus ();
                SetWindowPos (s_hToolTipWindow , NULL , 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW);
                s_hToolTipWindowVisible = FALSE;
                SetFocus (hFocusWindow);
            }
            return;
        }

        // Check if the window is registered
        TUT_tdstWndDefinition * p_stWndDef = hWnd ? TUT_g_oTutorialManager . m_fnp_stFindWndDefinition (hWnd) : NULL;
        if (! p_stWndDef)
        {
            if (s_hToolTipWindowVisible)
            {
                HWND hFocusWindow = GetFocus ();
                SetWindowPos (s_hToolTipWindow , NULL , 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW);
                s_hToolTipWindowVisible = FALSE;
                SetFocus (hFocusWindow);
            }
            return;
        }

        csStringToShow = p_stWndDef -> csName;

        // Put the string into the clipboard
        OpenClipboard (NULL);
        HANDLE  hText  = GetClipboardData (CF_TEXT);
        char *  szText = hText  ? (char *) GlobalLock (hText) : NULL;
        CString csText = szText ? szText : "";
        GlobalUnlock (hText);
        if (csText != csStringToShow)
        {
            EmptyClipboard ();
            long lSize = csStringToShow . GetLength ();
            hText      = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE , lSize + 1);
            szText     = (char *) GlobalLock (hText);
            CopyMemory       (szText , (LPCTSTR) csStringToShow , lSize + 1);
            SetClipboardData (CF_TEXT , hText);
            GlobalUnlock     (hText);
        }
        CloseClipboard ();

        csStringToShow += " (";
        csStringToShow += TUT_g_oTutorialManager . m_fn_szGetWndNameType (p_stWndDef -> eType);
        csStringToShow += ")";
    }

    // Change the position and the size of the tool tip window
    HDC  hDC = GetDC (s_hToolTipWindow);
    SIZE stSize;
    GetTextExtentPoint32 (hDC , csStringToShow , csStringToShow . GetLength () , & stSize);

    MoveWindow (s_hToolTipWindow , stMousePos . x + 4 , stMousePos . y - stSize . cy - 1 ,
                                   stSize . cx + 6 , stSize. cy + 2 , TRUE);

    RECT stClientRect;
    GetClientRect (s_hToolTipWindow , & stClientRect);

    hDC = GetDC (s_hToolTipWindow);

    RECT stRect = stClientRect;
    stRect . right = stRect . left + 2;
    FillRect     (hDC , & stRect , (HBRUSH) (COLOR_INFOBK + 1));
    stRect = stClientRect;
    stRect . left = stRect . right - 2;
    FillRect     (hDC , & stRect , (HBRUSH) (COLOR_INFOBK + 1));

    SetBkMode    (hDC , OPAQUE);
    SetBkColor   (hDC , GetSysColor (COLOR_INFOBK  ));
    SetTextColor (hDC , GetSysColor (COLOR_INFOTEXT));
    TextOut      (hDC , 2 , 0 , csStringToShow , csStringToShow . GetLength ());
    ReleaseDC    (s_hToolTipWindow , hDC);

    // If the tool tip window is not visible, show it
    if (! s_hToolTipWindowVisible)
    {
        HWND hFocusWindow = GetFocus ();
        SetWindowPos (s_hToolTipWindow , NULL , 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
        if (! bVariableNameFound)  SetFocus (hFocusWindow);
        s_hToolTipWindowVisible = TRUE;
    }

    if (bVariableNameFound)
    {
        SetWindowPos ( hWnd , s_hToolTipWindow , 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
    }
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - constructor
//-----------------------------------------------------------------------------
TUT_CTutorialManager::TUT_CTutorialManager ()
{
    m_p_stCurrentUserWindow            =  NULL;

    m_lCurrentMaxDuration              =   400;
    m_lCurrentMinSpeed                 =  1000;
    m_lCurrentAcceleration             = 10000;

    m_iDurationBetweenKeys             =   100;    // 100 ms between two keys
	m_bUseShift						   = FALSE;
	m_bUseAlt						   = FALSE;
	m_bUseControl					   = FALSE;

    m_xCurrentColor                    = GetSysColor (COLOR_WINDOWTEXT);
    m_lCurrentHeight                   =    10;
    m_lCurrentAlignment                = PFA_LEFT;

    m_bUseRightButton                  = FALSE;
    m_bUseDoubleClic                   = FALSE;
    m_bUsePosition                     = FALSE;
    m_bUseDrag                         = FALSE;
    m_bUseDrop                         = FALSE;

    m_stCurrentPopupMenu . hMenu       =     0;
    m_stCurrentPopupMenu . hWindow     =     0;
    m_stCurrentPopupMenu . iX          =     0;
    m_stCurrentPopupMenu . iY          =     0;

    m_hDebugWnd                        =  NULL;

    m_uiMainThreadID                   =     0;
	m_bRunMode						   = FALSE;

    m_iNumberOfParameter               =     0;
    m_iNumberOfResult                  =     0;

	m_stLogFile . bOpen				   = FALSE;
	m_stLogFile . csName			   =    "";
	m_stLogFile . pFile				   =  NULL;

	m_stDebugLogFile . bOpen		   = FALSE;
	m_stDebugLogFile . csName		   =    "";
	m_stDebugLogFile . pFile		   =  NULL;

    m_bInsideAGoto                     = FALSE;
    m_csLabelToFind                    =    "";

	m_uiTimerNumber					   =     0;
	m_bBackGroundActive				   = FALSE;

    m_oArrayOfWnd . SetSize (0 , 100);

	m_oListOfCommands . RemoveAll ();
	m_bInPause						   = FALSE;
	m_xLastCommandTime				   = (clock_t)0;
	m_lDragDrop						   = 0;
	m_csDragDropElement . Empty ();
	m_eDragDropType					   = TUT_eNone;

    if (LoadLibrary ("RICHED32.DLL") == NULL)
        MessageBox (NULL , "Unable to load the RICHED32.DLL file. Rich text edit will not be available" , "Tutorial" ,
                    MB_OK + MB_ICONSTOP + MB_TASKMODAL);

	// Create a timer with a function that will be called every 20 milliseconds
	m_uiTimerNumber = SetTimer (NULL , 0 , 20 , TUT_gs_fn_vCallbackTimerFunction);

    // Create a global event for communication between the two threads
    TUT_g_hMainThreadIdleEvent = CreateEvent (NULL , TRUE , FALSE , NULL);
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - destructor
//-----------------------------------------------------------------------------
TUT_CTutorialManager::~TUT_CTutorialManager ()
{
    if (m_uiTimerNumber != 0)  KillTimer (NULL , m_uiTimerNumber);

    m_fn_vClearAllList (TRUE);
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_vRegisterWnd
// Register a CWnd
//-----------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vRegisterWnd (HWND _hWnd , const CString _csName , TUT_tdeWndType _eType)
{
    // Don't register control with no name
    if (_csName . IsEmpty ())  return;
	if (_hWnd == NULL )
	{
        CString csText;
        csText = "Warning : the handle of the control '" + _csName + "' is null";
        MessageBox (NULL , csText , "Tutorial" , MB_OK + MB_ICONSTOP + MB_TASKMODAL);
		return;
	}

    int  iIndex;
    BOOL bFound = m_fn_bSearchWndDefinition (_csName , & iIndex);
    if (bFound)    // control with same name already exists
    {
        TUT_tdstWndDefinition * p_stOldWndDefinition = m_oArrayOfWnd [iIndex];
		/*
        CString csText;
        csText = "Warning : the control '" + _csName + "' is already registered";
        MessageBox (NULL , csText , "Tutorial" , MB_OK + MB_ICONSTOP + MB_TASKMODAL);
		*/
        // If the control is already registered, remove the previous one
        p_stOldWndDefinition -> hWnd    = _hWnd;
        p_stOldWndDefinition -> csName  = _csName;
        p_stOldWndDefinition -> eType   = _eType;
    }
    else
    {
        // insert after iIndex
        TUT_tdstWndDefinition * p_stWndDefinition = new TUT_tdstWndDefinition;

        p_stWndDefinition -> hWnd         = _hWnd;
        p_stWndDefinition -> csName       = _csName;
        p_stWndDefinition -> eType        = _eType;

        m_oArrayOfWnd . InsertAt (iIndex + 1 , p_stWndDefinition);
    }
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_bUnregisterWnd
// Unregister a CWnd. Return FALSE if not found
//-----------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bUnregisterWnd (HWND _hWnd)
{
    int  iIndex;
    BOOL bFound = m_fn_bSearchWndDefinition (_hWnd , & iIndex);
    if (bFound)
    {
        TUT_tdstWndDefinition * p_stWndDef = m_oArrayOfWnd [iIndex];
        m_oArrayOfWnd . RemoveAt (iIndex);

        delete p_stWndDef;
        return TRUE;
    }
    return FALSE;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_vRegisterMenu
// Register a menu
//-----------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vRegisterMenu (HWND _hWnd , HMENU _hMenu , int _iX , int _iY)
{
    m_stCurrentPopupMenu . hMenu   = _hMenu;
    m_stCurrentPopupMenu . hWindow = _hWnd;
    m_stCurrentPopupMenu . iX      = _iX;
    m_stCurrentPopupMenu . iY      = _iY;
}

//-----------------------------------------------------------------------------
typedef struct TUT_stThreadInfo_
{
    TUT_CTutorialManager    * p_oTutorialManager;
    CString                   csFileName;
    AFX_MODULE_THREAD_STATE * pThreadState;
} TUT_tdstThreadInfo;


//-----------------------------------------------------------------------------
// TUT_fn_dwBeginThread
// Call the m_fn_iExecuteScript function
//-----------------------------------------------------------------------------
DWORD WINAPI TUT_fn_dwBeginThread (void * _pParameter)
{
    TUT_tdstThreadInfo      * p_stThreadInfo     = (TUT_tdstThreadInfo *) _pParameter;
    TUT_CTutorialManager    * p_oTutorialManager = p_stThreadInfo -> p_oTutorialManager;
    CString                   csFileName         = p_stThreadInfo -> csFileName;
    AFX_MODULE_THREAD_STATE * pOtherThreadState  = p_stThreadInfo -> pThreadState;
    delete p_stThreadInfo;

    // Decreaze the priority of the Tutorial thread
    SetThreadPriority (GetCurrentThread () , THREAD_PRIORITY_LOWEST);

    // Let the tutorial thread access the C++ objects of the main thread (change the map of the handles)
    AFX_MODULE_THREAD_STATE * pThreadState   = AfxGetModuleThreadState ();
    CHandleMap              * pSaveHandleMap = pThreadState -> m_pmapHWND;
    pThreadState -> m_pmapHWND = pOtherThreadState -> m_pmapHWND;

    // Execute the script
	p_oTutorialManager -> m_fn_vSetRunMode (TRUE);
    DWORD dwResult = (DWORD) p_oTutorialManager -> m_fn_iExecuteScript (csFileName);
	p_oTutorialManager -> m_fn_vSetRunMode (FALSE);

    // Close all the user window
    p_oTutorialManager -> m_fn_vCloseAllUserWindow ();

    // Close the log file if necessary
    p_oTutorialManager -> m_fn_vCloseAllLogFiles ();

    // Clear all lists
    p_oTutorialManager -> m_fn_vClearAllList (FALSE);

    // Restore the map of the handles
    pThreadState -> m_pmapHWND = pSaveHandleMap;

    return dwResult;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_vSetBackGroundActive
// Update the BackGround Flag
// Return 0 if no error
//-----------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vSetBackGroundActive (BOOL _bActive)
{
	m_bBackGroundActive = _bActive;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_vSetRunMode
// Update the RunMode flag
// Return 0 if no error
//-----------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vSetRunMode	(BOOL _bRun)
{
	m_bRunMode = _bRun;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_bNotInBackGround
//-----------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bNotInBackGround (void)
{
	return (! m_bBackGroundActive && ! m_bRunMode);
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_bIsRunning
//-----------------------------------------------------------------------------
BOOL TUT_CTutorialManager::m_fn_bIsRunning(void)
{
	return m_bRunMode;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_vLaunchScript
// Execute a tutorial script by creating a new thread
// Return 0 if no error
//-----------------------------------------------------------------------------
void TUT_CTutorialManager::m_fn_vLaunchScript (const CString _csFileName)
{
    m_uiMainThreadID = GetCurrentThreadId ();

    TUT_tdstThreadInfo * p_stThreadInfo  = new TUT_tdstThreadInfo;    // Will be deleted by the TUT_fn_dwBeginThread function
    p_stThreadInfo -> p_oTutorialManager = this;
    p_stThreadInfo -> csFileName         = _csFileName;
    p_stThreadInfo -> pThreadState       = AfxGetModuleThreadState ();

    DWORD dwThreadId;
    CreateThread (NULL , 0 , TUT_fn_dwBeginThread , p_stThreadInfo , 0 , & dwThreadId);
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecutScript
// Execute a tutorial script
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteScript (const CString _csFileName)
{
    int  iReturnValue = TUT_C_iNoError;
    char szLine     [1024];

    // Open the file
    FILE * pFile = fopen (_csFileName , "rt");
    if (! pFile)
    {
        iReturnValue = TUT_C_iError_FileNotFound;
        m_fn_vShowError (iReturnValue , _csFileName , 0 , NULL);
        return iReturnValue;;
    }

    // Create the context
    TUT_tdstContextDefinition * p_stContextDef = new TUT_tdstContextDefinition;
    p_stContextDef -> csFileName  = _csFileName;
    p_stContextDef -> iLineNumber = 0;
    m_oListOfContext . AddHead (p_stContextDef);

    // Read the file
    while (iReturnValue == TUT_C_iNoError && fgets (szLine , 256 , pFile) != NULL)
    {
        char szCopy [1024];
        strcpy (szCopy , szLine);

        p_stContextDef -> iLineNumber ++;
        iReturnValue = m_fn_iExecuteScriptAction (szCopy , pFile);
    }

    // Close the file
    fclose (pFile);

    // Test if there is an error
    if (iReturnValue == TUT_C_iNoError && m_bInsideAGoto)  iReturnValue = TUT_C_iError_LabelNotFound;

    if (iReturnValue != TUT_C_iNoError && iReturnValue != TUT_C_iQuitImmediately)
        m_fn_vShowError (iReturnValue , _csFileName , p_stContextDef -> iLineNumber , szLine);

    // When closing a file, we delete the local labels and local variables
    m_fn_vDeleteLocalLabelAndVariable ();

    // Delete the context
    delete m_oListOfContext . RemoveHead ();

    return iReturnValue;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecutScriptAction
// Execute a tutorial script action (one line of a script file)
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteScriptAction (char * _szLine , FILE * pFile)
{
    // To be sure all the messages of the other threads have been proceeded
    // Debug window
    //if (m_hDebugWnd )  m_fn_vWriteText (m_hDebugWnd , "=> WaitforSingleObject");
    WaitForSingleObject (TUT_g_hMainThreadIdleEvent , INFINITE);
    // Debug window
    //if (m_hDebugWnd )  m_fn_vWriteText (m_hDebugWnd , "<= WaitforSingleObject");

    // If the Escape key is pressed, quit immediately
    if (GetAsyncKeyState (VK_ESCAPE) & 0x8000)  return TUT_C_iQuitImmediately;

    if (_szLine [strlen (_szLine) - 1] == 0x0A)  _szLine [strlen (_szLine) - 1] = 0;    // Remove "end of line" character

    // Debug window
    if (m_hDebugWnd && m_fn_bMustActionBeExecuted () && m_bInsideAGoto == FALSE)  m_fn_vWriteText (m_hDebugWnd , _szLine);

    // Debug Log File
    if (m_stDebugLogFile . bOpen && m_fn_bMustActionBeExecuted () && m_bInsideAGoto == FALSE)
	{
		fprintf (m_stDebugLogFile . pFile , "%s\n" , _szLine);
		fflush  (m_stDebugLogFile . pFile);
	}

    char * szAction = m_fn_szStrtok (_szLine , '(');
    m_fn_vPreprocessString (szAction);
    if (szAction == NULL || strlen (szAction) == 0 || szAction [0] == ';')  return TUT_C_iNoError;

    char * szParameter = m_fn_szStrtok (NULL , ')');

    if (* szAction == TUT_C_cBeginningOfVariable)
    {
        TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szAction);
        if (p_stVariableDef)  szAction = (char *) (LPCTSTR) p_stVariableDef -> csValue;
                        else  return TUT_C_iError_UnknownVariable;
    }

    // Special treatment for the label action
    if (stricmp (szAction , TUT_C_szLabel              ) == 0)  return m_fn_iExecuteLabel               (szParameter , pFile);
    if (m_bInsideAGoto)  return TUT_C_iNoError;

    // Special treatment for the test actions
    if (stricmp (szAction , TUT_C_szIf                 ) == 0)  return m_fn_iExecuteIf                  (szParameter);
    if (stricmp (szAction , TUT_C_szOrIf               ) == 0)  return m_fn_iExecuteOrIf                (szParameter);
    if (stricmp (szAction , TUT_C_szElse               ) == 0)  return m_fn_iExecuteElse                (szParameter);
    if (stricmp (szAction , TUT_C_szEndIf              ) == 0)  return m_fn_iExecuteEndIf               (szParameter);

    m_fn_vAddAnActionForTheTest ();
    if (! m_fn_bMustActionBeExecuted ())  return TUT_C_iNoError;

    // Special treatment for right button, double clic, position, drag and drop
    m_fn_vTestRightButtonDoubleClicAndPosition ();

    // Other actions
    if (stricmp (szAction , TUT_C_szClic               ) == 0)  return m_fn_iExecuteClic                (szParameter);
    if (stricmp (szAction , TUT_C_szOpenWindow         ) == 0)  return m_fn_iExecuteOpenWindow          (szParameter);
    if (stricmp (szAction , TUT_C_szWriteText          ) == 0)  return m_fn_iExecuteWriteText           (szParameter);
    if (stricmp (szAction , TUT_C_szPause              ) == 0)  return m_fn_iExecutePause               (szParameter);
    if (stricmp (szAction , TUT_C_szSetEditText        ) == 0)  return m_fn_iExecuteSetEditText         (szParameter);
    if (stricmp (szAction , TUT_C_szCloseWindow        ) == 0)  return m_fn_iExecuteCloseWindow         (szParameter);
    if (stricmp (szAction , TUT_C_szSetFocus           ) == 0)  return m_fn_iExecuteSetFocus            (szParameter);
    if (stricmp (szAction , TUT_C_szSelectWindow       ) == 0)  return m_fn_iExecuteSelectWindow        (szParameter);
    if (stricmp (szAction , TUT_C_szSelectLine         ) == 0)  return m_fn_iExecuteSelectLine          (szParameter , FALSE);
    if (stricmp (szAction , TUT_C_szMinimizeWindow     ) == 0)  return m_fn_iExecuteChangeWindowState   (szParameter , szAction);
    if (stricmp (szAction , TUT_C_szMaximizeWindow     ) == 0)  return m_fn_iExecuteChangeWindowState   (szParameter , szAction);
    if (stricmp (szAction , TUT_C_szRestoreWindow      ) == 0)  return m_fn_iExecuteChangeWindowState   (szParameter , szAction);
    if (stricmp (szAction , TUT_C_szSetMouseSpeed      ) == 0)  return m_fn_iExecuteSetMouseSpeed       (szParameter);
    if (stricmp (szAction , TUT_C_szSetText            ) == 0)  return m_fn_iExecuteSetText             (szParameter);
    if (stricmp (szAction , TUT_C_szSet                ) == 0)  return m_fn_iExecuteSet                 (szParameter);
    if (stricmp (szAction , TUT_C_szGetSelection       ) == 0)  return m_fn_iExecuteGetSelection        (szParameter);
    if (stricmp (szAction , TUT_C_szSelect             ) == 0)  return m_fn_iExecuteSelect              (szParameter , FALSE);
    if (stricmp (szAction , TUT_C_szCall               ) == 0)  return m_fn_iExecuteCall                (szParameter);
    if (stricmp (szAction , TUT_C_szExpand             ) == 0)  return m_fn_iExecuteExpandOrCollapse    (szParameter ,  TRUE);
    if (stricmp (szAction , TUT_C_szCollapse           ) == 0)  return m_fn_iExecuteExpandOrCollapse    (szParameter , FALSE);
    if (stricmp (szAction , TUT_C_szListAllControl     ) == 0)  return m_fn_iExecuteListAllControl      (szParameter);
    if (stricmp (szAction , TUT_C_szMenu               ) == 0)  return m_fn_iExecuteMenu                (szParameter , FALSE);
    if (stricmp (szAction , TUT_C_szLoop               ) == 0)  return m_fn_iExecuteLoop                (szParameter , pFile);
    if (stricmp (szAction , TUT_C_szEndLoop            ) == 0)  return m_fn_iExecuteEndLoop             (szParameter , pFile);
    if (stricmp (szAction , TUT_C_szRightButton        ) == 0)  return m_fn_iExecuteRightButton         (szParameter);
    if (stricmp (szAction , TUT_C_szDoubleClic         ) == 0)  return m_fn_iExecuteDoubleClic          (szParameter);
    if (stricmp (szAction , TUT_C_szPopupMenu          ) == 0)  return m_fn_iExecuteMenu                (szParameter ,  TRUE);
    if (stricmp (szAction , TUT_C_szOpenDebugWindow    ) == 0)  return m_fn_iExecuteOpenDebugWindow     (szParameter);
    if (stricmp (szAction , TUT_C_szCloseDebugWindow   ) == 0)  return m_fn_iExecuteCloseDebugWindow    (szParameter);
    if (stricmp (szAction , TUT_C_szGetTitle           ) == 0)  return m_fn_iExecuteGetTitle            (szParameter);
    if (stricmp (szAction , TUT_C_szGetText            ) == 0)  return m_fn_iExecuteGetText             (szParameter);
    if (stricmp (szAction , TUT_C_szIsVisible          ) == 0)  return m_fn_iExecuteIsVisible           (szParameter);
    if (stricmp (szAction , TUT_C_szExit               ) == 0)  return m_fn_iExecuteExit                (szParameter , pFile);
    if (stricmp (szAction , TUT_C_szHalt               ) == 0)  return m_fn_iExecuteHalt                (szParameter);
    if (stricmp (szAction , TUT_C_szGetNumberOfChildren) == 0)  return m_fn_iExecuteGetNumberOfChildren (szParameter);
    if (stricmp (szAction , TUT_C_szGetChild           ) == 0)  return m_fn_iExecuteGetChild            (szParameter);
    if (stricmp (szAction , TUT_C_szGetParent          ) == 0)  return m_fn_iExecuteGetParent           (szParameter);
    if (stricmp (szAction , TUT_C_szAdd                ) == 0)  return m_fn_iExecuteArithmeticOperation (szParameter , TUT_eAdd);
    if (stricmp (szAction , TUT_C_szSub                ) == 0)  return m_fn_iExecuteArithmeticOperation (szParameter , TUT_eSub);
    if (stricmp (szAction , TUT_C_szMul                ) == 0)  return m_fn_iExecuteArithmeticOperation (szParameter , TUT_eMul);
    if (stricmp (szAction , TUT_C_szDiv                ) == 0)  return m_fn_iExecuteArithmeticOperation (szParameter , TUT_eDiv);
    if (stricmp (szAction , TUT_C_szGetParameters      ) == 0)  return m_fn_iExecuteGetParameters       (szParameter);
    if (stricmp (szAction , TUT_C_szOpenLogFile        ) == 0)  return m_fn_iExecuteOpenLogFile         (szParameter , & m_stLogFile);
    if (stricmp (szAction , TUT_C_szCloseLogFile       ) == 0)  return m_fn_iExecuteCloseLogFile        (szParameter , & m_stLogFile);
    if (stricmp (szAction , TUT_C_szClearLogFile       ) == 0)  return m_fn_iExecuteClearLogFile        (szParameter , & m_stLogFile);
    if (stricmp (szAction , TUT_C_szPosition           ) == 0)  return m_fn_iExecutePosition            (szParameter);
    if (stricmp (szAction , TUT_C_szGoto               ) == 0)  return m_fn_iExecuteGoto                (szParameter , pFile);
    if (stricmp (szAction , TUT_C_szIsChecked          ) == 0)  return m_fn_iExecuteIsChecked           (szParameter);
    if (stricmp (szAction , TUT_C_szIsEnabled          ) == 0)  return m_fn_iExecuteIsEnabled           (szParameter);
    if (stricmp (szAction , TUT_C_szDrag               ) == 0)  return m_fn_iExecuteDrag                (szParameter);
    if (stricmp (szAction , TUT_C_szDrop               ) == 0)  return m_fn_iExecuteDrop                (szParameter);
    if (stricmp (szAction , TUT_C_szUnselect           ) == 0)  return m_fn_iExecuteUnselect            (szParameter);
    if (stricmp (szAction , TUT_C_szGetMenuItem        ) == 0)  return m_fn_iExecuteGetMenuItem         (szParameter , FALSE);
    if (stricmp (szAction , TUT_C_szGetPopupMenuItem   ) == 0)  return m_fn_iExecuteGetMenuItem         (szParameter ,  TRUE);
    if (stricmp (szAction , TUT_C_szFree               ) == 0)  return m_fn_iExecuteFree                (szParameter);
    if (stricmp (szAction , TUT_C_szGetCount           ) == 0)  return m_fn_iExecuteGetCount            (szParameter);
    if (stricmp (szAction , TUT_C_szMessageBox         ) == 0)  return m_fn_iExecuteMessageBox          (szParameter);
    if (stricmp (szAction , TUT_C_szSetResult          ) == 0)  return m_fn_iExecuteSetResult           (szParameter);
    if (stricmp (szAction , TUT_C_szGetResult          ) == 0)  return m_fn_iExecuteGetResult           (szParameter);
    if (stricmp (szAction , TUT_C_szKey                ) == 0)  return m_fn_iExecuteKey                 (szParameter);
    if (stricmp (szAction , TUT_C_szPress              ) == 0)  return m_fn_iExecutePressOrRelease      (szParameter ,  TRUE);
    if (stricmp (szAction , TUT_C_szRelease            ) == 0)  return m_fn_iExecutePressOrRelease      (szParameter , FALSE);
    if (stricmp (szAction , TUT_C_szSetKeySpeed        ) == 0)  return m_fn_iExecuteSetKeySpeed         (szParameter);
    if (stricmp (szAction , TUT_C_szIsExisting         ) == 0)  return m_fn_iExecuteIsExisting          (szParameter);
    if (stricmp (szAction , TUT_C_szGetLength          ) == 0)  return m_fn_iExecuteGetLength           (szParameter);
    if (stricmp (szAction , TUT_C_szFindString         ) == 0)  return m_fn_iExecuteFindString          (szParameter);
    if (stricmp (szAction , TUT_C_szExtractString      ) == 0)  return m_fn_iExecuteExtractString       (szParameter);
    if (stricmp (szAction , TUT_C_szBeep               ) == 0)  return m_fn_iExecuteBeep                (szParameter);
    if (stricmp (szAction , TUT_C_szPlay               ) == 0)  return m_fn_iExecutePlay                (szParameter);
    if (stricmp (szAction , TUT_C_szIsSelected         ) == 0)  return m_fn_iExecuteIsSelected          (szParameter);
    if (stricmp (szAction , TUT_C_szVar                ) == 0)  return m_fn_iExecuteVar                 (szParameter);
    if (stricmp (szAction , TUT_C_szGlobalVar          ) == 0)  return m_fn_iExecuteGlobalVar           (szParameter);
    if (stricmp (szAction , TUT_C_szOpenDebugLogFile   ) == 0)  return m_fn_iExecuteOpenLogFile         (szParameter , & m_stDebugLogFile);
    if (stricmp (szAction , TUT_C_szCloseDebugLogFile  ) == 0)  return m_fn_iExecuteCloseLogFile        (szParameter , & m_stDebugLogFile);
    if (stricmp (szAction , TUT_C_szClearDebugLogFile  ) == 0)  return m_fn_iExecuteClearLogFile        (szParameter , & m_stDebugLogFile);
    if (stricmp (szAction , TUT_C_szIsInList		   ) == 0)  return m_fn_iExecuteIsInList            (szParameter);
    if (stricmp (szAction , TUT_C_szSaveContext        ) == 0)  return m_fn_iExecuteSaveContext         (szParameter);
    if (stricmp (szAction , TUT_C_szRestoreContext     ) == 0)  return m_fn_iExecuteRestoreContext      (szParameter);
    if (stricmp (szAction , TUT_C_szAddSelect		   ) == 0)  return m_fn_iExecuteSelect				(szParameter , TRUE);
    if (stricmp (szAction , TUT_C_szAddSelectLine	   ) == 0)  return m_fn_iExecuteSelectLine			(szParameter , TRUE);
    if (stricmp (szAction , TUT_C_szGetFocusWindow	   ) == 0)  return m_fn_iExecuteGetFocusWindow		(szParameter);
    if (stricmp (szAction , TUT_C_szHasFocus		   ) == 0)  return m_fn_iExecuteHasFocus		    (szParameter);
    if (stricmp (szAction , TUT_C_szSelectText		   ) == 0)  return m_fn_iExecuteSelectText		    (szParameter);

    // 3D view specific
    if (stricmp (szAction , TUT_C_sz3DGetPosition      ) == 0)  return m_fn_iExecute3dGetPosition       (szParameter);
    if (stricmp (szAction , TUT_C_sz3DClic3dView       ) == 0)  return m_fn_iExecute3dClic3dView        (szParameter);
    if (stricmp (szAction , TUT_C_sz3DIsProtected      ) == 0)  return m_fn_iExecute3dIsProtected       (szParameter);
    if (stricmp (szAction , TUT_C_sz3DMove3d           ) == 0)  return m_fn_iExecute3dMove3d            (szParameter);
    if (stricmp (szAction , TUT_C_sz3DSelectModel      ) == 0)  return m_fn_iExecute3dSelectModel       (szParameter);
    if (stricmp (szAction , TUT_C_sz3DGetModel         ) == 0)  return m_fn_iExecute3dGetModel          (szParameter);
    if (stricmp (szAction , TUT_C_sz3DGetNumberOfModels) == 0)  return m_fn_iExecute3dGetNumberOfModels (szParameter);
    if (stricmp (szAction , TUT_C_sz3DIsAvailable      ) == 0)  return m_fn_iExecute3dIsAvailable       (szParameter);
    if (stricmp (szAction , TUT_C_sz3DGetPrefix        ) == 0)  return m_fn_iExecute3dGetPrefix         (szParameter);
    if (stricmp (szAction , TUT_C_sz3DIsExistingModel  ) == 0)  return m_fn_iExecute3dIsExistingModel   (szParameter);
    if (stricmp (szAction , TUT_C_sz3DGetStatusBarText ) == 0)  return m_fn_iExecute3dGetStatusBarText  (szParameter);

    return TUT_C_iError_UnknownCommand;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteClic
// Execute a "clic" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteClic (char * _szParameter)
{
    // Get parameter : name of the control
    int    iNumberOfParameter;
    char * szControlName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szControlName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find control with the given name
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szControlName);
    if (p_stWndDefinition == NULL)                  return TUT_C_iError_UnknownControl;

    HWND hControl = p_stWndDefinition -> hWnd;
    if (! IsWindow        (hControl))  return TUT_C_iError_InvalidHandle;
    if (! IsWindowVisible (hControl))  return TUT_C_iError_InvisibleControl;
    if (! IsWindowEnabled (hControl))  return TUT_C_iError_DisabledControl;

    m_fn_vMoveMouseCursor (hControl);
	m_fn_vActivateWindow  (GetParent (hControl));
    m_fn_vClickOnControl  (hControl);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteOpenWindow
// Execute a "open window" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteOpenWindow (char * _szParameter)
{
    // Get parameters : name of the window, coordinates
    int    iNumberOfParameter;
    char * szWindowName;
    char * szLeft , * szRight , * szTop , * szBottom;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 5 , 5 , & iNumberOfParameter , & szWindowName ,
                                               & szLeft , & szTop , & szRight , & szBottom);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int    iLeft   = m_fn_iConvertToInt (szLeft);
    int    iRight  = m_fn_iConvertToInt (szRight);
    int    iTop    = m_fn_iConvertToInt (szTop);
    int    iBottom = m_fn_iConvertToInt (szBottom);

    if (iLeft == TUT_C_iErrorNotANumber || iRight  == TUT_C_iErrorNotANumber ||
        iTop  == TUT_C_iErrorNotANumber || iBottom == TUT_C_iErrorNotANumber)  return TUT_C_iError_InvalidNumber;

    if (iLeft < 0 || iRight  > 100 || iLeft >= iRight ||
        iTop  < 0 || iBottom > 100 || iTop  >= iBottom)  return TUT_C_iError_InvalidCoordinates;

    // Create the window
    HWND hEditView = m_fn_hOpenUserWindow (szWindowName , iLeft , iTop , iRight , iBottom);
    m_fn_vRegisterWnd (hEditView , szWindowName , TUT_eUserWindow);

    m_p_stCurrentUserWindow = m_fnp_stFindWndDefinition (szWindowName);
    ASSERT (m_p_stCurrentUserWindow);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteWriteText
// Execute a "write text" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteWriteText (char * _szParameter)
{
//	m_fn_vSetGetLastError (TUT_C_iNoError);

    // Get parameter : text to write
    int    iNumberOfParameter;
    char * szText1 , * szText2 , * szText3 , * szText4;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 4 , & iNumberOfParameter ,
                                               & szText1 , & szText2 , & szText3 , & szText4);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (m_p_stCurrentUserWindow == NULL && !m_stLogFile . bOpen)
	{
        //m_fn_vSetGetLastError (TUT_C_iError_NoWindowToWriteText);
        return TUT_C_iNoError;
	}

    // Insert the new text
    CString csText = szText1;
    if (iNumberOfParameter >= 2)  csText += szText2;
    if (iNumberOfParameter >= 3)  csText += szText3;
    if (iNumberOfParameter >= 4)  csText += szText4;
    if (m_p_stCurrentUserWindow)  m_fn_vWriteText (m_p_stCurrentUserWindow -> hWnd , csText);

    if (m_stLogFile . bOpen)
    {
        if (csText . GetLength () > 0 && csText [csText . GetLength () - 1] == '\\')
             fprintf (m_stLogFile . pFile , "%s" , (LPCTSTR) csText . Left (csText . GetLength () - 1));
        else fprintf (m_stLogFile . pFile , "%s\n" , (LPCTSTR) csText);

		fflush (m_stLogFile . pFile);
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iPause
// Execute a "pause" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecutePause (char * _szParameter)
{
    // Get parameter : duration in millisecond
    int    iNumberOfParameter;
    char * szDuration;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 1 , & iNumberOfParameter , & szDuration);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int    iDuration;
    if (iNumberOfParameter == 1)
    {
        iDuration = m_fn_iConvertToInt (szDuration);
        if (iDuration == TUT_C_iErrorNotANumber)  return TUT_C_iError_InvalidNumber;
    }
    else iDuration = 3600000;    // No parameter -> wait for one hour

    m_fn_vSleep (iDuration);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSetEditText
// Execute a "set edit text" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSetEditText (char * _szParameter)
{
    // Get parameters : name of the text edit control, text to put into, line number (for multi-line text edit control)
    int    iNumberOfParameter;
    char * szEditName;
    char * szText;
    char * szLineNumber;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 3 , & iNumberOfParameter , & szEditName , & szText , & szLineNumber);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find control with the given name
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szEditName);
    if (p_stWndDefinition == NULL)                    return TUT_C_iError_UnknownTextEdit;
    if (p_stWndDefinition -> eType != TUT_eTextEdit)  return TUT_C_iError_TextEditRequired;

    HWND hTextEdit = p_stWndDefinition -> hWnd;
    if (! IsWindow        (hTextEdit))  return TUT_C_iError_InvalidTextEditHandle;
    if (! IsWindowVisible (hTextEdit))  return TUT_C_iError_InvisibleControl;
    if (! IsWindowEnabled (hTextEdit))  return TUT_C_iError_DisabledControl;

    if (iNumberOfParameter == 3)    // Multi-line control
    {
        if ((GetWindowLong (hTextEdit , GWL_STYLE) & ES_MULTILINE) == 0)  return TUT_C_iError_MultiLineRequired;

        int iNumberOfLine = SendMessage (hTextEdit , EM_GETLINECOUNT , 0 , 0);

        int iLineNumber = m_fn_iConvertToInt (szLineNumber);
        if (iLineNumber == TUT_C_iErrorNotANumber)  return TUT_C_iError_InvalidNumber;
        if (iLineNumber > iNumberOfLine + 1)        return TUT_C_iError_InvalidLineNumber;

        // Add a new line if necessary
        if (iLineNumber == iNumberOfLine + 1)
        {
            int iNumberOfChar = GetWindowTextLength (hTextEdit);
            char szText [3] = { 13 , 10 , 0 };
            SendMessage (hTextEdit , EM_SETSEL , iNumberOfChar , iNumberOfChar);
            SendMessage (hTextEdit , EM_REPLACESEL , FALSE /* No Undo */ , (LPARAM) (LPCTSTR) szText);
        }

        // Scroll the text if necessary
        CRect stTextEditRect;
        GetClientRect (hTextEdit, & stTextEditRect);
        HDC   hDC = GetDC (hTextEdit);
        SIZE  stCharacterSize;
        GetTextExtentPoint32 (hDC , "M" , 1 , & stCharacterSize);
        stCharacterSize . cy -= 3;
        int   iNbLine = max (stTextEditRect . Height () / stCharacterSize . cy , 1);

        BOOL  bOk;
        int   iCounter = 0;
        int   iFirstLine;
        do
        {
            iFirstLine = SendMessage (hTextEdit , EM_GETFIRSTVISIBLELINE , 0 , 0) + 1;

            bOk = FALSE;
            if (iLineNumber < iFirstLine)                SendMessage (hTextEdit , EM_LINESCROLL , 0 , -1);
            else
            if (iLineNumber > iFirstLine + iNbLine - 1)  SendMessage (hTextEdit , EM_LINESCROLL , 0 ,  1);
            else bOk = TRUE;
        } while (bOk == FALSE && ++iCounter < 100);    // To prevent from infinite loop
        POINT stPos;
        stPos . x = 5;
        stPos . y = (iLineNumber - iFirstLine) * stCharacterSize . cy + stCharacterSize . cy / 2;
        ClientToScreen (hTextEdit , & stPos);
        m_fn_vMoveMouseCursor (stPos);

        m_fn_vActivateWindow  (hTextEdit);

        // Send the text
        int iBeginningOfLine = SendMessage (hTextEdit , EM_LINEINDEX , iLineNumber - 1 , 0);
        int iEndOfLine       = iLineNumber <= iNumberOfLine ? SendMessage (hTextEdit , EM_LINEINDEX , iLineNumber , 0) - 2
                                                            : GetWindowTextLength (hTextEdit);
        SendMessage (hTextEdit , EM_SETSEL , iBeginningOfLine , iEndOfLine);

        int iNumChar = iBeginningOfLine;

        for (UINT i = 1 ; i <= strlen (szText) ; i ++)
        {
            // Insert one character
//???            char szChar [2] = { 0 , 0 };
//???            szChar [0] = szText [i - 1];
//???            SendMessage (hTextEdit , EM_REPLACESEL , FALSE /* No Undo */ , (LPARAM) szChar);
            SendMessage (hTextEdit , WM_CHAR , szText [i - 1] , 1);

            // Move the position
            iNumChar ++;
            SendMessage (hTextEdit , EM_SETSEL , iNumChar , iNumChar);

            if (i < strlen (szText))  m_fn_vSleep (m_iDurationBetweenKeys);
        }
    }
    else    // Single-line control
    {
        m_fn_vMoveMouseCursor (hTextEdit);
        m_fn_vActivateWindow  (hTextEdit);
        m_fn_vSetTextInEdit   (hTextEdit , szText);
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteCloseWindow
// Execute a "close window" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteCloseWindow (char * _szParameter)
{
    // Get parameters : name of the window
    int    iNumberOfParameter;
    char * szWindowName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szWindowName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find the window with the given name
    int iIndex;
    BOOL bFound = m_fn_bSearchWndDefinition (szWindowName , & iIndex);
    if (! bFound)  return TUT_C_iError_UnknownWindow;

    // Close the parent window, remove it from the list and delete it
    m_fn_vCloseUserWindow (iIndex);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSetFocus
// Execute a "set focus" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSetFocus (char * _szParameter)
{
    // Get parameters : name of the window
    int    iNumberOfParameter;
    char * szWindowName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szWindowName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find the window with the given name
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szWindowName);
    if (p_stWndDefinition == NULL)  return TUT_C_iError_UnknownWindow;

    m_fn_vActivateWindow (p_stWndDefinition -> hWnd);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSelectWindow
// Execute a "select window" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSelectWindow (char * _szParameter)
{
    // Get parameter : name of the window
    int    iNumberOfParameter;
    char * szWindowName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szWindowName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find the window with the given name
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szWindowName);
    if (p_stWndDefinition == NULL || p_stWndDefinition -> eType != TUT_eUserWindow)  return TUT_C_iError_UnknownWindow;

    m_p_stCurrentUserWindow = p_stWndDefinition;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSelectLine
// Execute a "select line" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSelectLine (char * _szParameter, BOOL _bAddToSelection)
{
    // Get parameters : name of the control, line number to select
    int    iNumberOfParameter;
    char * szControlName;
    char * szLineNumber;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szLineNumber);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int    iLineNumber = m_fn_iConvertToInt (szLineNumber);
    if (iLineNumber == TUT_C_iErrorNotANumber)         return TUT_C_iError_InvalidNumber;

    // Find the control with the given name
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szControlName);
    if (p_stWndDefinition == NULL)                    return TUT_C_iError_UnknownControl;

    if (p_stWndDefinition -> eType == TUT_eComboBox)    // Selection in a combo box
    {
        HWND hComboBox = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hComboBox))  return TUT_C_iError_InvalidComboBoxHandle;
        if (! IsWindowVisible (hComboBox))  return TUT_C_iError_InvisibleControl;
        if (! IsWindowEnabled (hComboBox))  return TUT_C_iError_DisabledControl;
		if (_bAddToSelection)				return TUT_C_iError_NoMultipleSelection;

        // Compute position of the button to drop down the list
        CRect ComboRect;
        GetWindowRect (hComboBox , & ComboRect);
        POINT PosDropButton;
        PosDropButton . x = ComboRect . right - ComboRect . Height () / 2;
        PosDropButton . y = ComboRect . CenterPoint () . y;

        m_fn_vMoveMouseCursor      (PosDropButton);
        m_fn_vActivateWindow       (hComboBox);
        m_fn_vSelectLineInComboBox (hComboBox , iLineNumber);
    }
    else
    if (p_stWndDefinition -> eType == TUT_eListBox)     // Selection in a list box
    {
        HWND hListBox = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hListBox))  return TUT_C_iError_InvalidListBoxHandle;
        if (! IsWindowVisible (hListBox))  return TUT_C_iError_InvisibleControl;
        if (! IsWindowEnabled (hListBox))  return TUT_C_iError_DisabledControl;
		if (_bAddToSelection && 
			! (GetWindowLong (hListBox , GWL_STYLE) & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)))	return TUT_C_iError_NoMultipleSelection;

        m_fn_vSelectLineInListBox  (hListBox , iLineNumber, _bAddToSelection);
    }
    else
    if (p_stWndDefinition -> eType == TUT_eListCtrl)    // Selection in a list control
    {
        HWND hListCtrl = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hListCtrl))  return TUT_C_iError_InvalidListCtrlHandle;
        if (! IsWindowVisible (hListCtrl))  return TUT_C_iError_InvisibleControl;
        if (! IsWindowEnabled (hListCtrl))  return TUT_C_iError_DisabledControl;
		if (_bAddToSelection)				return TUT_C_iError_NoMultipleSelection;

        m_fn_vSelectLineInListCtrl (hListCtrl , iLineNumber);
    }
    else return TUT_C_iError_ListOrComboRequired;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteChangeWindowState
// Execute a "minimize window", "maximize window" or "restore window" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteChangeWindowState (char * _szParameter , char * _szAction)
{
    // Get parameters : name of the window
    int    iNumberOfParameter;
    char * szWindowName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szWindowName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find the window with the given name
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szWindowName);
    if (p_stWndDefinition == NULL)                     return TUT_C_iError_UnknownMdiWindow;
    if (p_stWndDefinition -> eType != TUT_eMdiWindow)  return TUT_C_iError_MdiWindowRequired;

    HWND hMdiWindow = p_stWndDefinition -> hWnd;
    HWND hParent    = GetParent (hMdiWindow);

    if (! IsWindow        (hMdiWindow))  return TUT_C_iError_InvalidMdiHandle;
    if (! IsWindowVisible (hMdiWindow))  return TUT_C_iError_InvisibleControl;
    if (! IsWindowEnabled (hMdiWindow))  return TUT_C_iError_DisabledControl;

    if (! IsWindow        (hParent   ))  return TUT_C_iError_InvalidParentMdiHandle;
    if (! IsWindowVisible (hParent   ))  return TUT_C_iError_InvisibleControl;
    if (! IsWindowEnabled (hParent   ))  return TUT_C_iError_DisabledControl;

    if (stricmp (_szAction , TUT_C_szMinimizeWindow) == 0) ShowWindow  (hMdiWindow , SW_SHOWMINIMIZED);
    else
    if (stricmp (_szAction , TUT_C_szMaximizeWindow) == 0) SendMessage (hParent , WM_MDIMAXIMIZE , (WPARAM) hMdiWindow , 0);
    else
    if (stricmp (_szAction , TUT_C_szRestoreWindow ) == 0) SendMessage (hParent , WM_MDIRESTORE  , (WPARAM) hMdiWindow , 0);
    else ASSERT (0);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSetMouseSpeed
// Execute a "set mouse speed" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSetMouseSpeed (char * _szParameter)
{
    // Get parameters : max duration of a move (in millisecond), minimum speed of the mouse (pixel/second)
    // and maximum acceleration (pixel/second²)

    int    iNumberOfParameter;
    char * szMaxDuration , * szMinSpeed , * szAcceleration;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 3 , & iNumberOfParameter ,
                                               & szMaxDuration , & szMinSpeed , & szAcceleration);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    long lMaxDuration = (long) m_fn_iConvertToInt (szMaxDuration);
    if (lMaxDuration == (long) TUT_C_iErrorNotANumber || lMaxDuration <= 0)  return TUT_C_iError_InvalidNumber;
    m_lCurrentMaxDuration = lMaxDuration;

    if (iNumberOfParameter >= 2)
    {
        long lMinSpeed = (long) m_fn_iConvertToInt (szMinSpeed);
        if (lMinSpeed == (long) TUT_C_iErrorNotANumber || lMinSpeed <= 0)  return TUT_C_iError_InvalidNumber;
        m_lCurrentMinSpeed = lMinSpeed;
    }

    if (iNumberOfParameter >= 3)
    {
        long lAcceleration = (long) m_fn_iConvertToInt (szAcceleration);
        if (lAcceleration == (long) TUT_C_iErrorNotANumber || lAcceleration <= 0)  return TUT_C_iError_InvalidNumber;
        m_lCurrentAcceleration = lAcceleration;
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSetText
// Execute a "set text" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSetText (char * _szParameter)
{
    // Get parameters : color name, height of characters, text alignment

    int    iNumberOfParameter;
    char * szColor , * szHeight , * szTextAlignment;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 3 , & iNumberOfParameter ,
                                               & szColor , & szHeight , & szTextAlignment);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (* szColor != 0 && ! m_fn_bFindColor (szColor , & m_xCurrentColor))  return TUT_C_iError_UnknownColor;

    if (iNumberOfParameter >= 2 && * szHeight != 0)
    {
        long lHeight = (long) m_fn_iConvertToInt (szHeight);
        if (lHeight == (long) TUT_C_iErrorNotANumber || lHeight <= 0)  return TUT_C_iError_InvalidNumber;
        m_lCurrentHeight = lHeight;
    }

    if (iNumberOfParameter >= 3)
    {
        if (stricmp (szTextAlignment , TUT_C_szTextAlignment_Left  ) == 0)  m_lCurrentAlignment = PFA_LEFT;    else
        if (stricmp (szTextAlignment , TUT_C_szTextAlignment_Right ) == 0)  m_lCurrentAlignment = PFA_RIGHT;   else
        if (stricmp (szTextAlignment , TUT_C_szTextAlignment_Center) == 0)  m_lCurrentAlignment = PFA_CENTER;
        else return TUT_C_iError_InvalidTextAlignment;
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSet
// Execute a "set" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSet (char * _szParameter)
{
    // Get parameters : variable name, value1 and value2
    int    iNumberOfParameter;
    char * szVariableName , * szValue1 , * szValue2;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 3 , & iNumberOfParameter , & szVariableName ,
                                               & szValue1 , & szValue2);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    p_stVariableDef -> csValue = szValue1;
    if (iNumberOfParameter == 3)  p_stVariableDef -> csValue += szValue2;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetSelection
// Execute a "get selection" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetSelection (char * _szParameter)
{
    // Get parameters : control name and variable name
    int    iNumberOfParameter;
    char * szControlName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szControlName);
    if (! p_stWndDef)  return TUT_C_iError_UnknownControl;

    char szSelection [512];
    if (p_stWndDef -> eType == TUT_eListBox)
    {
        HWND hListBox   = p_stWndDef -> hWnd;
        if (! IsWindow        (hListBox))  return TUT_C_iError_InvalidListBoxHandle;
        if (! IsWindowVisible (hListBox))  return TUT_C_iError_InvisibleControl;

        int  iSelection = SendMessage (hListBox , LB_GETCURSEL , 0 , 0);
        if (iSelection != LB_ERR)  SendMessage (hListBox , LB_GETTEXT , iSelection , (LPARAM) szSelection);
    }
    else
    if (p_stWndDef -> eType == TUT_eComboBox)
    {
        HWND hComboBox  = p_stWndDef -> hWnd;
        if (! IsWindow        (hComboBox))  return TUT_C_iError_InvalidComboBoxHandle;
        if (! IsWindowVisible (hComboBox))  return TUT_C_iError_InvisibleControl;

        int  iSelection = SendMessage (hComboBox , CB_GETCURSEL , 0 , 0);
        if (iSelection != CB_ERR)  SendMessage (hComboBox , CB_GETLBTEXT , iSelection , (LPARAM) szSelection);
    }
    else
    if (p_stWndDef -> eType == TUT_eTreeCtrl)
    {
        HWND hTreeCtrl = p_stWndDef -> hWnd;
        if (! IsWindow        (hTreeCtrl))  return TUT_C_iError_InvalidTreeBoxHandle;
        if (! IsWindowVisible (hTreeCtrl))  return TUT_C_iError_InvisibleControl;

        HTREEITEM hSelection = TreeView_GetSelection (hTreeCtrl);
        if (hSelection)  strcpy (szSelection , m_fn_csGetTreeCtrlItemText (hTreeCtrl , hSelection));
    }
    else
    if (p_stWndDef -> eType == TUT_eListCtrl)
    {
        HWND hListCtrl = p_stWndDef -> hWnd;
        if (! IsWindow         (hListCtrl))  return TUT_C_iError_InvalidListCtrlHandle;
        if (! IsWindowVisible  (hListCtrl))  return TUT_C_iError_InvisibleControl;

        int iSelectedItem = ListView_GetNextItem (hListCtrl , -1 , LVNI_ALL | LVNI_SELECTED);
        if (iSelectedItem != -1)
        {
            ListView_GetItemText (hListCtrl , iSelectedItem , 0 , szSelection , 511);
        }
        else  * szSelection = 0;    // No selection -> return ""
    }
    else return TUT_C_iError_SelectCtrlRequired;

    p_stVariableDef -> csValue = szSelection;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSelect
// Execute a "select" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSelect (char * _szParameter, BOOL _bAddToSelection)
{
    m_fn_vSetGetLastError (TUT_C_iNoError);

    // Get parameters : control name and text to select
    int    iNumberOfParameter;
    char * szControlName , * szTextToSelect;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szTextToSelect);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition  (szControlName);
    if (! p_stWndDefinition)  return TUT_C_iError_UnknownControl;

    if (p_stWndDefinition -> eType == TUT_eListBox)        // Selection in a list box
    {
        HWND hListBox = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hListBox))  return TUT_C_iError_InvalidListBoxHandle;
        if (! IsWindowVisible (hListBox))  return TUT_C_iError_InvisibleControl;
        if (! IsWindowEnabled (hListBox))  return TUT_C_iError_DisabledControl;
		if (_bAddToSelection && 
			! (GetWindowLong (hListBox , GWL_STYLE) & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)))	return TUT_C_iError_NoMultipleSelection;

        int iLineNumber = SendMessage (hListBox , LB_FINDSTRINGEXACT , -1 , (LPARAM) szTextToSelect);
        if (iLineNumber == LB_ERR)
        {
            m_fn_vSetGetLastError (TUT_C_iError_ListBoxItemNotFound);
            return TUT_C_iNoError;
        }

        m_fn_vSelectLineInListBox (hListBox , iLineNumber + 1, _bAddToSelection);
    }
    else
    if (p_stWndDefinition -> eType == TUT_eComboBox)       // Selection in a combo box
    {
        HWND hComboBox = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hComboBox))  return TUT_C_iError_InvalidComboBoxHandle;
        if (! IsWindowVisible (hComboBox))  return TUT_C_iError_InvisibleControl;
        if (! IsWindowEnabled (hComboBox))  return TUT_C_iError_DisabledControl;
		if (_bAddToSelection)				return TUT_C_iError_NoMultipleSelection;

        int iLineNumber = SendMessage (hComboBox , CB_FINDSTRINGEXACT , -1 , (LPARAM) szTextToSelect);
        if (iLineNumber == CB_ERR)
        {
            m_fn_vSetGetLastError (TUT_C_iError_ComboBoxItemNotFound);
            return TUT_C_iNoError;
        }

        // Compute position of the button to drop down the list
        CRect ComboRect;
        GetWindowRect (hComboBox , & ComboRect);
        POINT PosDropButton;
        PosDropButton . x = ComboRect . right - ComboRect . Height () / 2;
        PosDropButton . y = ComboRect . CenterPoint () . y;

        m_fn_vMoveMouseCursor      (PosDropButton);
        m_fn_vActivateWindow       (p_stWndDefinition -> hWnd);
        m_fn_vSelectLineInComboBox (hComboBox , iLineNumber + 1);
    }
    else
    if (p_stWndDefinition -> eType == TUT_eTreeCtrl)        // Selection in a tree box
    {
        HWND hTreeBox = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hTreeBox))  return TUT_C_iError_InvalidTreeBoxHandle;
        if (! IsWindowVisible (hTreeBox))  return TUT_C_iError_InvisibleControl;
        if (! IsWindowEnabled (hTreeBox))  return TUT_C_iError_DisabledControl;
		if (_bAddToSelection)				return TUT_C_iError_NoMultipleSelection;

        if (! m_fn_bSelectItemInTreeBox (hTreeBox , szTextToSelect))
        {
            m_fn_vSetGetLastError (TUT_C_iError_TreeCtrlItemNotFound);
        }
    }
    else
    if (p_stWndDefinition -> eType == TUT_eListCtrl)        // Selection in a list control
    {
        HWND hListCtrl = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hListCtrl))  return TUT_C_iError_InvalidListCtrlHandle;
        if (! IsWindowVisible (hListCtrl))  return TUT_C_iError_InvisibleControl;
        if (! IsWindowEnabled (hListCtrl))  return TUT_C_iError_DisabledControl;
		if (_bAddToSelection)				return TUT_C_iError_NoMultipleSelection;

        LV_FINDINFO stFindInfo;
        stFindInfo . flags = LVFI_STRING;
        stFindInfo . psz   = szTextToSelect;
        int iLineNumber = ListView_FindItem (hListCtrl , -1 , & stFindInfo);
        if (iLineNumber == -1)
        {
            m_fn_vSetGetLastError (TUT_C_iError_ListCtrlItemNotFound);
            return TUT_C_iNoError;
        }

        m_fn_vSelectLineInListCtrl (hListCtrl , iLineNumber + 1);
    }
    else return TUT_C_iError_SelectCtrlRequired;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteCall
// Execute a "call" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteCall (char * _szParameter)
{
    // Get parameters : file name and parameters
    int    iNumberOfParameter;
    char * szFileName;
    char * a_szParameter [9];

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 10 , & iNumberOfParameter , & szFileName , a_szParameter ,
                                               a_szParameter + 1 , a_szParameter + 2 , a_szParameter + 3 , a_szParameter + 4 ,
                                               a_szParameter + 5 , a_szParameter + 6 , a_szParameter + 7 , a_szParameter + 8);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    m_iNumberOfParameter = iNumberOfParameter - 1;
    for (int i = 0 ; i < m_iNumberOfParameter ; i ++)
        m_a_csParameter [i] = a_szParameter [i];

    iReturnValue = TUT_CTutorialManager::m_fn_iExecuteScript (szFileName);
    if (iReturnValue != TUT_C_iNoError)  return TUT_C_iQuitImmediately;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteExpandOrCollapse
// Execute a "expand" or "collapse" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteExpandOrCollapse (char * _szParameter , BOOL _bExpand)
{
    // Get parameters : tree box name
    int    iNumberOfParameter;
    char * szTreeBoxName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szTreeBoxName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szTreeBoxName);
    if (! p_stWndDefinition)                          return TUT_C_iError_UnknownTreeBox;
    if (p_stWndDefinition -> eType != TUT_eTreeCtrl)  return TUT_C_iError_TreeBoxRequired;

    HWND hTreeCtrl = p_stWndDefinition -> hWnd;
    HTREEITEM hSelectedItem = TreeView_GetSelection (hTreeCtrl);
    HTREEITEM hChildItem    = hSelectedItem ? TreeView_GetChild (hTreeCtrl , hSelectedItem) : NULL;
    if (hChildItem)
        m_fn_vEnsureVisibleInTree (hTreeCtrl , hChildItem , _bExpand);
    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteListAllControl
// Execute a "list all control" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteListAllControl (char * _szParameter)
{
    // No parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (m_p_stCurrentUserWindow == NULL)  return TUT_C_iError_NoWindowToWriteText;

    int iNbCtrl = m_oArrayOfWnd . GetSize ();
    for (int iIndex = 0 ; iIndex < iNbCtrl ; iIndex ++)
    {
        TUT_tdstWndDefinition * p_stWndDef = m_oArrayOfWnd [iIndex];
        CString csText = p_stWndDef -> csName;
        csText += " (";
        csText += m_fn_szGetWndNameType (p_stWndDef -> eType);
        csText += ")";
        m_fn_vWriteText (m_p_stCurrentUserWindow -> hWnd , csText);
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteMenu
// Execute a "menu" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteMenu (char * _szParameter , BOOL _bPopupMenu)
{
    // Parameters
    int    iNumberOfParameter;
    char * a_szMenuName [9];
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 9 , & iNumberOfParameter , a_szMenuName ,
                                               a_szMenuName + 1 , a_szMenuName + 2 , a_szMenuName + 3 , a_szMenuName + 4 ,
                                               a_szMenuName + 5 , a_szMenuName + 6 , a_szMenuName + 7 , a_szMenuName + 8);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    HWND   hWindowWithMenu;
    HMENU  hMenu;
    CPoint stMenuPos;
    if (_bPopupMenu)
    {
        hWindowWithMenu = m_stCurrentPopupMenu . hWindow;
        hMenu           = m_stCurrentPopupMenu . hMenu;
        stMenuPos . x   = m_stCurrentPopupMenu . iX;
        stMenuPos . y   = m_stCurrentPopupMenu . iY;
    }
    else
    {
        hWindowWithMenu = AfxGetMainWnd () -> m_hWnd;
        hMenu           = GetMenu (hWindowWithMenu);
        CRect stWindowRect;
        GetWindowRect (hWindowWithMenu , & stWindowRect);
        stMenuPos = stWindowRect . TopLeft ();
    }
    if (hMenu == NULL               )  return TUT_C_iError_NoMenuAvailable;
    if (! IsMenu   (hMenu          ))  return TUT_C_iError_InvalidMenuHandle;
    if (! IsWindow (hWindowWithMenu))  return TUT_C_iError_InvalidMenuWndHandle;

    for (int iMenuLevel = 0 ; iMenuLevel < iNumberOfParameter ; iMenuLevel ++)
    {
        int iNumberOfItem = GetMenuItemCount (hMenu);
        for (int i = 0 ; i < iNumberOfItem ; i ++)
        {
            char szMenuName [256];
            GetMenuString (hMenu , i , szMenuName , 255 , MF_BYPOSITION);
            CString csMenuName = szMenuName;

            int iPosChar = csMenuName . Find ('&');    // Remove the '&' character
            if (iPosChar != -1)  csMenuName = csMenuName . Left (iPosChar) + csMenuName . Mid (iPosChar + 1);

            iPosChar = csMenuName . Find ('\t');    // Remove the accelerator key
            if (iPosChar != -1)  csMenuName = csMenuName . Left (iPosChar);

            if (csMenuName . CompareNoCase (a_szMenuName [iMenuLevel]) == 0)
            {
                CRect stItemRect;
                BOOL  bGoodCoordinate = GetMenuItemRect (GetDesktopWindow () , hMenu , i , & stItemRect);

                if (bGoodCoordinate)
                {
                    CPoint stItemPos = stMenuPos + stItemRect . CenterPoint ();
                    m_fn_vMoveMouseCursor (stItemPos);
                }
                else
                {
                    DWORD dwError = GetLastError ();
                }

                if (GetSubMenu (hMenu , i))
                    SendMessage (hWindowWithMenu , WM_INITMENUPOPUP , (WPARAM) GetSubMenu (hMenu , i) , MAKELPARAM (i , FALSE));
                PostMessage (hWindowWithMenu , WM_COMMAND , GetMenuItemID (hMenu , i) , 0);
                break;
            }
        }
        if (i == iNumberOfItem)  // Not found
            return TUT_C_iError_UnknownMenuName;

        if (iMenuLevel < iNumberOfParameter - 1)
        {
            hMenu = GetSubMenu (hMenu , i);
            if (hMenu == NULL)  return TUT_C_iError_NotASubMenu;
        }
    }

    if (_bPopupMenu)    // If it is a popup menu, we have to close it
    {
        PostThreadMessage (m_uiMainThreadID , WM_LBUTTONDOWN , 0 , MAKELPARAM (2000 , 2000));
        PostThreadMessage (m_uiMainThreadID , WM_LBUTTONUP   , 0 , MAKELPARAM (2000 , 2000));
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteLoop
// Execute a "loop" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteLoop (char * _szParameter , FILE * pFile)
{
    // Parameters
    int    iNumberOfParameter;
    char * szVariableName , * szBeginValue , * szEndValue , * szStep;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 3 , 4 , & iNumberOfParameter , & szVariableName ,
                                               & szBeginValue , & szEndValue , & szStep);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    long                         lBeginValue     = (long) m_fn_iConvertToInt (szBeginValue);
    long                         lEndValue       = (long) m_fn_iConvertToInt (szEndValue);
    long                         lStep           = iNumberOfParameter >= 4 ? (long) m_fn_iConvertToInt (szStep) : 1;

    if (lBeginValue == (long) TUT_C_iErrorNotANumber || lEndValue == (long) TUT_C_iErrorNotANumber ||
        lStep       == (long) TUT_C_iErrorNotANumber)  return TUT_C_iError_InvalidNumber;

    // Create a new loop definition
    TUT_tdstLoopDefinition * p_stLoopDef = new TUT_tdstLoopDefinition;
    p_stLoopDef -> p_stVariable  = p_stVariableDef;
    p_stLoopDef -> lCurrentValue = lBeginValue;
    p_stLoopDef -> lEndValue     = lEndValue;
    p_stLoopDef -> lStep         = lStep;
    fgetpos (pFile , & p_stLoopDef -> xPosInFile);
    m_fn_vSetValueToVariable (p_stLoopDef -> p_stVariable , lBeginValue);

    // And add it to the heap of loop definitions
    m_oListOfLoop . AddHead (p_stLoopDef);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteEndLoop
// Execute a "end loop" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteEndLoop (char * _szParameter , FILE * pFile)
{
    // Check that there is no parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Check if at least one loop is open
    if (m_oListOfLoop . GetCount () == 0)  return TUT_C_iError_EndLoopWithoutLoop;

    // Get the loop definition
    TUT_tdstLoopDefinition * p_stLoopDef = m_oListOfLoop . GetHead ();

    // Increment the current value
    p_stLoopDef -> lCurrentValue += p_stLoopDef -> lStep;
    m_fn_vSetValueToVariable (p_stLoopDef -> p_stVariable , p_stLoopDef -> lCurrentValue);

    // Check if this is the end of the loop
    if ((p_stLoopDef -> lStep >= 0 && p_stLoopDef -> lCurrentValue <= p_stLoopDef -> lEndValue) ||
        (p_stLoopDef -> lStep <  0 && p_stLoopDef -> lCurrentValue >= p_stLoopDef -> lEndValue))
    {
        fsetpos (pFile , &  p_stLoopDef -> xPosInFile);    // Go to the beginning of the loop
    }
    else delete m_oListOfLoop . RemoveHead ();        // End of the loop : remove the loop from the heap

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIf
// Execute a "if" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteIf (char * _szParameter)
{
    // Check if we are in the not executed block of a test
    if (m_oListOfTest . GetCount () != 0)
    {
        // Get the current test
        TUT_tdstTestDefinition * p_stTestDef = m_oListOfTest . GetHead ();
        // If we are in the not executed block of the test ...
        if (p_stTestDef -> bTestVerified ^ p_stTestDef -> ePositionInTest != TUT_eAfterElse)
        {
            p_stTestDef -> iNumberOfSkippedTest ++;    // ... we skip this test
            return TUT_C_iNoError;                     // and we do nothing else
        }
    }

    // Parameters
    int    iNumberOfParameter;
    char * szParameter1 , * szTestOperator , * szParameter2;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 3 , & iNumberOfParameter ,
                                               & szParameter1 , & szTestOperator , & szParameter2);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;
    if (iNumberOfParameter == 2)         return TUT_C_iError_ParameterRequired;  // Only 1 or 3 parameters

    // Check if the test is verified
    BOOL bTestVerified;
    if (iNumberOfParameter == 1)  bTestVerified = m_fn_bIsParameterTrue (szParameter1 , & iReturnValue);
                            else  bTestVerified = m_fn_bIsTestVerified  (szTestOperator , szParameter1 , szParameter2 , & iReturnValue);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // If this 'If' is inside another 'If', this 'If' is an action for the previous 'If'
    m_fn_vAddAnActionForTheTest ();

    // Create a new test definition
    TUT_tdstTestDefinition * p_stTestDef = new TUT_tdstTestDefinition;
    p_stTestDef -> ePositionInTest       = TUT_eAfterIf;
    p_stTestDef -> bTestVerified         = bTestVerified;
    p_stTestDef -> iNumberOfSkippedTest  = 0;

    // Add this test definition into the heap of test
    m_oListOfTest . AddHead (p_stTestDef);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteOrIf
// Execute a "or if" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteOrIf (char * _szParameter)
{
    // Parameters
    int    iNumberOfParameter;
    char * szParameter1 , * szTestOperator , * szParameter2;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 3 , & iNumberOfParameter ,
                                               & szParameter1 , & szTestOperator , & szParameter2);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;
    if (iNumberOfParameter == 2)         return TUT_C_iError_ParameterRequired;    // Only 1 or 3 parameters

    // There must be a previous "If" action
    if (m_oListOfTest . GetCount () == 0)          return TUT_C_iError_OrIfWithoutAnIf;

    // We get the current test
    TUT_tdstTestDefinition * p_stTestDef = m_oListOfTest . GetHead ();

    // If we are in the not executed block of the test, we do nothing
    if (p_stTestDef -> iNumberOfSkippedTest != 0)  return TUT_C_iNoError;

    // The previous test must be the previous action
    if (p_stTestDef -> ePositionInTest != TUT_eAfterIf)  return TUT_C_iError_OrIfWithoutAnIf;

    // Check if the test is verified
    BOOL bTestVerified;
    if (iNumberOfParameter == 1)  bTestVerified = m_fn_bIsParameterTrue (szParameter1 , & iReturnValue);
                            else  bTestVerified = m_fn_bIsTestVerified  (szTestOperator , szParameter1 , szParameter2 , & iReturnValue);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (bTestVerified)  p_stTestDef -> bTestVerified = TRUE;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteElse
// Execute a "else" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteElse (char * _szParameter)
{
    // Parameters
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // There must be a previous "If" action
    if (m_oListOfTest . GetCount () == 0)          return TUT_C_iError_ElseWithoutAnIf;

    // We get the current test
    TUT_tdstTestDefinition * p_stTestDef = m_oListOfTest . GetHead ();

    // If we are in the not executed block of the test, we do nothing
    if (p_stTestDef -> iNumberOfSkippedTest != 0)  return TUT_C_iNoError;

    if (p_stTestDef -> ePositionInTest != TUT_eBeforeElse)  return TUT_C_iError_ElseInTheWrongPlace;

    p_stTestDef -> ePositionInTest = TUT_eAfterElse;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteEndIf
// Execute a "end if" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteEndIf (char * _szParameter)
{
    // Parameters
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // There must be a previous "If" action
    if (m_oListOfTest . GetCount () == 0)          return TUT_C_iError_EndIfWithoutAnIf;

    // We get the current test
    TUT_tdstTestDefinition * p_stTestDef = m_oListOfTest . GetHead ();

    // If we are in the not executed block of the test, we close the test
    if (p_stTestDef -> iNumberOfSkippedTest != 0)
    {
        p_stTestDef -> iNumberOfSkippedTest --;
        return TUT_C_iNoError;
    }

    // Remove the test definition from the heap
    delete m_oListOfTest . RemoveHead ();

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteRightButton
// Execute a "right button" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteRightButton (char * _szParameter)
{
    // Parameters
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    m_bUseRightButton = TRUE;    // Tell that the next action will use the right button instead of using the left button

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteDoubleClic
// Execute a "double clic" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteDoubleClic (char * _szParameter)
{
    // Parameters
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    m_bUseDoubleClic = TRUE;    // Tell that the next action will use a double clic instead of using a simple clic

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteOpenDebugWindow
// Execute a "open debug window" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteOpenDebugWindow (char * _szParameter)
{
    // Get parameters : coordinates of the window
    int    iNumberOfParameter;
    char * szLeft , * szRight , * szTop , * szBottom;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 4 , 4 , & iNumberOfParameter ,
                                               & szLeft , & szTop , & szRight , & szBottom);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int    iLeft   = m_fn_iConvertToInt (szLeft);
    int    iRight  = m_fn_iConvertToInt (szRight);
    int    iTop    = m_fn_iConvertToInt (szTop);
    int    iBottom = m_fn_iConvertToInt (szBottom);

    if (iLeft == TUT_C_iErrorNotANumber || iRight  == TUT_C_iErrorNotANumber ||
        iTop  == TUT_C_iErrorNotANumber || iBottom == TUT_C_iErrorNotANumber)  return TUT_C_iError_InvalidNumber;

    if (iLeft < 0 || iRight  > 100 || iLeft >= iRight ||
        iTop  < 0 || iBottom > 100 || iTop  >= iBottom)  return TUT_C_iError_InvalidCoordinates;

    if (m_hDebugWnd != NULL)  return TUT_C_iError_DebugWndAlreadyOpen;

    // Create the window
    m_hDebugWnd = m_fn_hOpenUserWindow ("Debug" , iLeft , iTop , iRight , iBottom);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteCloseDebugWindow
// Execute a "close debug window" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteCloseDebugWindow (char * _szParameter)
{
    // No parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (m_hDebugWnd == NULL)  return TUT_C_iError_DebugWndNotOpen;

    HWND hParent = GetParent (m_hDebugWnd);
    ASSERT (hParent);

    DestroyWindow (m_hDebugWnd);
    DestroyWindow (hParent);
    m_hDebugWnd = NULL;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetTitle
// Execute a "get title" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetTitle (char * _szParameter)
{
    // Get parameters : window name and variable name
    int    iNumberOfParameter;
    char * szWindowName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szWindowName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szWindowName);
    if (! p_stWndDef)  return TUT_C_iError_UnknownWindow;

    if (p_stWndDef -> eType != TUT_eWindow && p_stWndDef -> eType != TUT_eMdiWindow)
        return TUT_C_iError_WindowOrMdiRequired;

    HWND hWindow = p_stWndDef -> hWnd;
    if (! IsWindow        (hWindow))  return TUT_C_iError_InvalidWndOrMdiHandle;
    if (! IsWindowVisible (hWindow))  return TUT_C_iError_InvisibleControl;

    char szTitle [512];
    GetWindowText (hWindow , szTitle , 511);

    p_stVariableDef -> csValue = szTitle;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetText
// Execute a "get text" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetText (char * _szParameter)
{
    // Get parameters : control name and variable name
    int    iNumberOfParameter;
    char * szControlName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szControlName);
    if (! p_stWndDef)  return TUT_C_iError_UnknownControl;

    if (p_stWndDef -> eType != TUT_eButton && p_stWndDef -> eType != TUT_eTextEdit)
        return TUT_C_iError_CtrlWithTextRequired;

    HWND hControl = p_stWndDef -> hWnd;
    if (! IsWindow        (hControl))  return TUT_C_iError_InvalidHandle;
    if (! IsWindowVisible (hControl))  return TUT_C_iError_InvisibleControl;

    char szText [512];
    GetWindowText (hControl , szText , 511);

    p_stVariableDef -> csValue = szText;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIsVisible
// Execute a "is visible" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteIsVisible (char * _szParameter)
{
    // Get parameters : control name and variable name
    int    iNumberOfParameter;
    char * szControlName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szControlName);
    if (! p_stWndDef)  return TUT_C_iError_UnknownControl;

    HWND hControl = p_stWndDef -> hWnd;
    if (! IsWindow (hControl))  return TUT_C_iError_InvalidHandle;

    p_stVariableDef -> csValue = IsWindowVisible (hControl) ? TUT_C_szTrue : TUT_C_szFalse;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteExit
// Execute an "exit" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteExit (char * _szParameter , FILE * _pFile)
{
    // Check that there is no parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    fseek (_pFile , 0 , SEEK_END);
    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteHalt
// Execute a "halt" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteHalt (char * _szParameter)
{
    // Check that there is no parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    return TUT_C_iQuitImmediately;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetNumberOfChildren
// Execute a "get number of children" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetNumberOfChildren (char * _szParameter)
{
    // Get parameters : tree box name, item name and variable name
    int    iNumberOfParameter;
    char * szTreeBoxName , * szItemName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 3 , 3 , & iNumberOfParameter , & szTreeBoxName , & szItemName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find the tree control
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szTreeBoxName);
    if (! p_stWndDefinition)                          return TUT_C_iError_UnknownTreeBox;
    if (p_stWndDefinition -> eType != TUT_eTreeCtrl)  return TUT_C_iError_TreeBoxRequired;

    // Find the item inside the tree
    HWND      hTreeCtrl = p_stWndDefinition -> hWnd;
    HTREEITEM hTreeItem = m_fn_hFindStringInTree (hTreeCtrl , TreeView_GetRoot (hTreeCtrl) , szItemName);
    if (hTreeItem == NULL)  return TUT_C_iError_ItemNotFoundInTree;

    // Find the variable
    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    // Count the number of children
    int       iNumberOfChildren = 0;
    HTREEITEM hChildItem        = TreeView_GetChild (hTreeCtrl , hTreeItem);
    while (hChildItem)
    {
        iNumberOfChildren ++;
        hChildItem = TreeView_GetNextItem (hTreeCtrl , hChildItem , TVGN_NEXT);
    }

    // And store the result into the variable
    m_fn_vSetValueToVariable (p_stVariableDef , iNumberOfChildren);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetChild
// Execute a "get child" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetChild (char * _szParameter)
{
    // Get parameters : tree box name, item name, child number and variable name
    int    iNumberOfParameter;
    char * szTreeBoxName , * szItemName , * szChildNumber , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 4 , 4 , & iNumberOfParameter , & szTreeBoxName , & szItemName ,
                                                                                             & szChildNumber , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Get the child number
    int    iChildNumber = m_fn_iConvertToInt (szChildNumber);
    if (iChildNumber == TUT_C_iErrorNotANumber)  return TUT_C_iError_InvalidNumber;

    // Find the tree control
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szTreeBoxName);
    if (! p_stWndDefinition)                          return TUT_C_iError_UnknownTreeBox;
    if (p_stWndDefinition -> eType != TUT_eTreeCtrl)  return TUT_C_iError_TreeBoxRequired;

    // Find the item inside the tree
    HWND      hTreeCtrl = p_stWndDefinition -> hWnd;
    HTREEITEM hTreeItem = m_fn_hFindStringInTree (hTreeCtrl , TreeView_GetRoot (hTreeCtrl) , szItemName);
    if (hTreeItem == NULL)  return TUT_C_iError_ItemNotFoundInTree;

    // Find the variable
    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    // Get the child
    HTREEITEM hChildItem = TreeView_GetChild (hTreeCtrl , hTreeItem);
    if (hChildItem == NULL)  return TUT_C_iError_ItemHasNoChild;
    for (int i = 1 ; i < iChildNumber ; i ++)
    {
        hChildItem = TreeView_GetNextItem (hTreeCtrl , hChildItem , TVGN_NEXT);
        if (hChildItem == NULL)  return TUT_C_iError_InvalidChildNumber;
    }

    p_stVariableDef -> csValue = m_fn_csGetTreeCtrlItemText (hTreeCtrl , hChildItem);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetParent
// Execute a "get parent" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetParent (char * _szParameter)
{
    // Get parameters : tree box name, item name and variable name
    int    iNumberOfParameter;
    char * szTreeBoxName , * szItemName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 3 , 3 , & iNumberOfParameter , & szTreeBoxName , & szItemName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition (szTreeBoxName);
    if (! p_stWndDefinition)                          return TUT_C_iError_UnknownTreeBox;
    if (p_stWndDefinition -> eType != TUT_eTreeCtrl)  return TUT_C_iError_TreeBoxRequired;

    HWND      hTreeCtrl = p_stWndDefinition -> hWnd;
    HTREEITEM hTreeItem = m_fn_hFindStringInTree (hTreeCtrl , TreeView_GetRoot (hTreeCtrl) , szItemName);
    if (hTreeItem == NULL)  return TUT_C_iError_ItemNotFoundInTree;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    HTREEITEM hParentItem = TreeView_GetParent (hTreeCtrl , hTreeItem);
    p_stVariableDef -> csValue = hParentItem ? m_fn_csGetTreeCtrlItemText (hTreeCtrl , hParentItem) : "";

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteArithmeticOperation
// Execute a "add", "sub", "mul" or "div" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteArithmeticOperation (char * _szParameter , TUT_tdeArithmeticOperation _eOperation)
{
    // Get parameters : variable name, parameter1 and parameter2
    int    iNumberOfParameter;
    char * szVariableName , * szParameter1 , * szParameter2;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 3 , & iNumberOfParameter , & szVariableName ,
                                               & szParameter1 , & szParameter2);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    double dParameter1 = m_fn_dConvertToDouble (szParameter1);
    if (dParameter1 == TUT_C_dErrorNotANumber)  return TUT_C_iError_InvalidNumber;

    double dParameter2;
    if (iNumberOfParameter == 2)
    {
        if (p_stVariableDef -> csValue . IsEmpty ())  return TUT_C_iError_EmptyVariable;
        dParameter2 = dParameter1;
        dParameter1 = m_fn_dConvertToDouble ((char *) (LPCTSTR) p_stVariableDef -> csValue);
        if (dParameter1 == TUT_C_dErrorNotANumber)  return TUT_C_iError_NotANumericVariable;
    }
    else
    {
        dParameter2 = m_fn_dConvertToDouble (szParameter2);
        if (dParameter2 == TUT_C_dErrorNotANumber)  return TUT_C_iError_InvalidNumber;
    }

    double dResultValue;
    switch (_eOperation)
    {
        case TUT_eAdd : dResultValue = dParameter1 + dParameter2;   break;
        case TUT_eSub : dResultValue = dParameter1 - dParameter2;   break;
        case TUT_eMul : dResultValue = dParameter1 * dParameter2;   break;
        case TUT_eDiv : if (dParameter2 == 0.0)  return TUT_C_iError_DivisionByZero;
                        dResultValue = dParameter1 / dParameter2;   break;
        default       : ASSERT (0);
    }

    p_stVariableDef -> csValue . Format ("%g" , dResultValue);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetParameters
// Execute a "get parameters" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetParameters (char * _szParameter)
{
    // Get parameters
    int    iNumberOfParameter;
    char * a_szVariableName [9];

    int    iReturnValue = m_fn_iGetParameters (_szParameter , m_iNumberOfParameter , m_iNumberOfParameter , & iNumberOfParameter , a_szVariableName ,
                                               a_szVariableName + 1 , a_szVariableName + 2 , a_szVariableName + 3 , a_szVariableName + 4 ,
                                               a_szVariableName + 5 , a_szVariableName + 6 , a_szVariableName + 7 , a_szVariableName + 8);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    for (int i = 0 ; i < iNumberOfParameter ; i ++)
    {
        TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (a_szVariableName [i]);
        if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

        p_stVariableDef -> csValue = m_a_csParameter [i];
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteOpenLogFile
// Execute a "open log file" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteOpenLogFile (char * _szParameter, TUT_tdstLogFileDefinition * _p_stLogFile)
{
    // Get parameter : name of the log file
    int    iNumberOfParameter;
    char * szLogFileName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szLogFileName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (_p_stLogFile -> bOpen)  return TUT_C_iError_LogFileAlreadyOpen;

    _p_stLogFile -> pFile = fopen (szLogFileName , "at");
    if (_p_stLogFile -> pFile == NULL)
    {
        DWORD dwError = GetLastError ();
        return TUT_C_iError_UnableToOpenLogFile;
    }

    _p_stLogFile -> bOpen  = TRUE;
    _p_stLogFile -> csName = szLogFileName;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteCloseLogFile
// Execute a "close log file" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteCloseLogFile (char * _szParameter, TUT_tdstLogFileDefinition * _p_stLogFile)
{
    // Check that there is no parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (!_p_stLogFile -> bOpen)  return TUT_C_iError_LogFileNotOpen;

    m_fn_vCloseLogFile (_p_stLogFile);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteClearLogFile
// Execute a "clear log file" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteClearLogFile (char * _szParameter, TUT_tdstLogFileDefinition * _p_stLogFile)
{
    // Check that there is no parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (! _p_stLogFile -> bOpen)  return TUT_C_iError_LogFileNotOpen;

    fclose (_p_stLogFile -> pFile);
    _p_stLogFile -> pFile = fopen (_p_stLogFile -> csName, "wt");

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecutePosition
// Execute a "position" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecutePosition (char * _szParameter)
{
    // Get parameter : X border, X position, Y border and Y position
    int    iNumberOfParameter;
    char * szXBorder , * szXPos , * szYBorder , * szYPos;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 4 , 4 , & iNumberOfParameter , & szXBorder , & szXPos , & szYBorder , & szYPos);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (stricmp (szXBorder , TUT_C_szBorderLeft  ) == 0)  m_bRightBorder  = FALSE;
    else
    if (stricmp (szXBorder , TUT_C_szBorderRight ) == 0)  m_bRightBorder  =  TRUE;
    else return TUT_C_iError_InvalidXBorder;

    if (stricmp (szYBorder , TUT_C_szBorderTop   ) == 0)  m_bBottomBorder = FALSE;
    else
    if (stricmp (szYBorder , TUT_C_szBorderBottom) == 0)  m_bBottomBorder = TRUE;
    else return TUT_C_iError_InvalidYBorder;

    if (szXPos [strlen (szXPos) - 1] == '%')
    {
        m_bPercentForX = TRUE;
        szXPos [strlen (szXPos) - 1] = 0;
    }
    else m_bPercentForX = FALSE;

    if (szYPos [strlen (szYPos) - 1] == '%')
    {
        m_bPercentForY = TRUE;
        szYPos [strlen (szYPos) - 1] = 0;
    }
    else m_bPercentForY = FALSE;

    m_iXPosition = m_fn_iConvertToInt (szXPos);
    m_iYPosition = m_fn_iConvertToInt (szYPos);
    if (m_iXPosition == TUT_C_iErrorNotANumber || m_iYPosition == TUT_C_iErrorNotANumber)
        return TUT_C_iError_InvalidNumberOrPercent;

    m_bUsePosition = TRUE;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteLabel
// Execute a "label" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteLabel (char * _szParameter , FILE * _pFile)
{
    // Get parameter : name of the label
    int    iNumberOfParameter;
    char * szLabelName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szLabelName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    fpos_t xPosInFile;
    fgetpos (_pFile , & xPosInFile);

    TUT_tdstLabelDefinition * p_stLabelDef = m_fnp_stFindLabel (szLabelName);
    if (p_stLabelDef)
    {
        if (p_stLabelDef -> xPosInFile != xPosInFile)  return TUT_C_iError_LabelsWithSameName;
    }
    else
    {
        p_stLabelDef = new TUT_tdstLabelDefinition;
        p_stLabelDef -> csName         = szLabelName;
        p_stLabelDef -> xPosInFile     = xPosInFile;
        p_stLabelDef -> iContextNumber = m_oListOfContext . GetCount ();
        m_oListOfLabel . AddTail (p_stLabelDef);
    }

    if (m_bInsideAGoto && m_csLabelToFind . CompareNoCase (szLabelName) == 0)
    {
        m_bInsideAGoto  = FALSE;
        m_csLabelToFind = "";
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGoto
// Execute a "goto" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGoto (char * _szParameter , FILE * _pFile)
{
    // Get parameter : name of the label
    int    iNumberOfParameter;
    char * szLabelName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szLabelName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstLabelDefinition * p_stLabelDef = m_fnp_stFindLabel (szLabelName);
    if (p_stLabelDef)
    {
        fsetpos (_pFile , & p_stLabelDef -> xPosInFile);
    }
    else
    {
        m_bInsideAGoto    = TRUE;
        m_csLabelToFind   = szLabelName;
    }

    // Leave all loops and tests
    while (m_oListOfLoop . GetCount ())
        delete m_oListOfLoop . RemoveTail ();
    while (m_oListOfTest . GetCount ())
        delete m_oListOfTest . RemoveTail ();

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIsChecked
// Execute a "is checked" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteIsChecked (char * _szParameter)
{
    // Get parameters : button name and variable name
    int    iNumberOfParameter;
    char * szButtonName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szButtonName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szButtonName);
    if (! p_stWndDef)                        return TUT_C_iError_UnknownButton;
    if (p_stWndDef -> eType != TUT_eButton)  return TUT_C_iError_ButtonRequired;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    HWND hButton = p_stWndDef -> hWnd;
    if (! IsWindow        (hButton))  return TUT_C_iError_InvalidButtonHandle;
    if (! IsWindowVisible (hButton))  return TUT_C_iError_InvisibleControl;

    p_stVariableDef -> csValue = SendMessage (hButton , BM_GETCHECK , 0 , 0) == BST_CHECKED ? TUT_C_szTrue : TUT_C_szFalse;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIsEnabled
// Execute a "is enabled" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteIsEnabled (char * _szParameter)
{
    // Get parameters : control name and variable name
    int    iNumberOfParameter;
    char * szControlName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szControlName);
    if (! p_stWndDef)  return TUT_C_iError_UnknownControl;

    HWND hControl = p_stWndDef -> hWnd;
    if (! IsWindow        (hControl))  return TUT_C_iError_InvalidHandle;
    if (! IsWindowVisible (hControl))  return TUT_C_iError_InvisibleControl;

    p_stVariableDef -> csValue = IsWindowEnabled (hControl) ? TUT_C_szTrue : TUT_C_szFalse;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteDrag
// Execute a "drag" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteDrag (char * _szParameter)
{
    // Check that there is no parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (m_bUseDrag || m_bUseDrop)  return TUT_C_iError_DragAtTheWrongPlace;
    m_bUseDrag = TRUE;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteDrop
// Execute a "drop" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteDrop (char * _szParameter)
{
    // Check that there is no parameter
    int    iNumberOfParameter;
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 0 , & iNumberOfParameter);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (m_bUseDrag || m_bUseDrop)  return TUT_C_iError_DropAtTheWrongPlace;
    m_bUseDrop = TRUE;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteUnselect
// Execute a "unselect" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteUnselect (char * _szParameter)
{
    m_fn_vSetGetLastError (TUT_C_iNoError);

    // Get parameters : list box name and text to unselect
    int    iNumberOfParameter;
    char * szListBoxName , * szTextToUnselect;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 2 , & iNumberOfParameter , & szListBoxName , & szTextToUnselect);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition  (szListBoxName);
    if (! p_stWndDefinition)                         return TUT_C_iError_UnknownListBox;
    if (p_stWndDefinition -> eType != TUT_eListBox)  return TUT_C_iError_ListBoxRequired;

    HWND hListBox = p_stWndDefinition -> hWnd;
    if (! IsWindow        (hListBox))  return TUT_C_iError_InvalidListBoxHandle;
    if (! IsWindowVisible (hListBox))  return TUT_C_iError_InvisibleControl;
    if (! IsWindowEnabled (hListBox))  return TUT_C_iError_DisabledControl;

    if ((GetWindowLong (hListBox , GWL_STYLE) & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) == 0)
        return TUT_C_iError_NoMultipleSelection;

    HWND hParentWnd = GetParent (hListBox);
    if (iNumberOfParameter == 1)    // Unselection of all items
    {
        SendMessage (hListBox , LB_SETSEL , FALSE , -1);
        PostMessage (hParentWnd , WM_COMMAND , (LBN_SELCHANGE << 16) + GetDlgCtrlID (hListBox) , (LPARAM) hListBox);
    }
    else                            // Unselection of one item
    {
        int iLineNumber = SendMessage (hListBox , LB_FINDSTRINGEXACT , -1 , (LPARAM) szTextToUnselect);
        if (iLineNumber == LB_ERR)
        {
            m_fn_vSetGetLastError (TUT_C_iError_ListBoxItemNotFound);
            return TUT_C_iNoError;
        }

        // To be sure the line will be visible
        SendMessage(hListBox , LVM_ENSUREVISIBLE , iLineNumber , FALSE);

        // Compute the position of the selected line to move the mouse to
        CRect ListPos;
        GetWindowRect (hListBox , & ListPos);
        int iTopIndex   = SendMessage (hListBox , LB_GETTOPINDEX   , 0 , 0);
        int iItemHeight = SendMessage (hListBox , LB_GETITEMHEIGHT , 0 , 0);
        CPoint Pos;
        Pos . x = min (ListPos . CenterPoint () . x , ListPos . left + 4 * iItemHeight);
        Pos . y = ListPos . top + iItemHeight / 2 + iItemHeight * (iLineNumber - iTopIndex);
        m_fn_vMoveMouseCursor (Pos);

        SendMessage (hListBox , LB_SETSEL , FALSE , iLineNumber);
        PostMessage (hParentWnd , WM_COMMAND , (LBN_SELCHANGE << 16) + GetDlgCtrlID (hListBox) , (LPARAM) hListBox);
    }

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetMenuItem
// Execute a "get meunu item" or "get popup menu item" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetMenuItem (char * _szParameter , BOOL _bPopupMenu)
{
    // Parameters
    int    iNumberOfParameter;
    char * a_szParameter [12];
    int    iReturnValue = m_fn_iGetParameters (_szParameter , 4 , 12 , & iNumberOfParameter ,
                                               a_szParameter      , a_szParameter +  1 , a_szParameter +  2 ,
                                               a_szParameter +  3 , a_szParameter +  4 , a_szParameter +  5 ,
                                               a_szParameter +  6 , a_szParameter +  7 , a_szParameter +  8 ,
                                               a_szParameter +  9 , a_szParameter + 10 , a_szParameter + 11);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;
    int    iNumberOfLevel = iNumberOfParameter - 3;

    HMENU  hMenu;
    if (_bPopupMenu)  hMenu = m_stCurrentPopupMenu . hMenu;
                else  hMenu = GetMenu (AfxGetMainWnd () -> m_hWnd);
    if (hMenu == NULL               )  return TUT_C_iError_NoMenuAvailable;
    if (! IsMenu   (hMenu          ))  return TUT_C_iError_InvalidMenuHandle;

    TUT_tdstVariableDefinition * p_stVariablePresent = m_fnp_stFindVariable (a_szParameter [iNumberOfParameter - 3]);
    TUT_tdstVariableDefinition * p_stVariableEnabled = m_fnp_stFindVariable (a_szParameter [iNumberOfParameter - 2]);
    TUT_tdstVariableDefinition * p_stVariableChecked = m_fnp_stFindVariable (a_szParameter [iNumberOfParameter - 1]);
    if (p_stVariablePresent == NULL || p_stVariableEnabled == NULL || p_stVariableChecked == NULL)
        return TUT_C_iError_UnknownVariable;

    int i;
    for (int iMenuLevel = 0 ; iMenuLevel < iNumberOfLevel ; iMenuLevel ++)
    {
        int iNumberOfItem = GetMenuItemCount (hMenu);
        for (i = 0 ; i < iNumberOfItem ; i ++)
        {
            char szMenuName [256];
            GetMenuString (hMenu , i , szMenuName , 255 , MF_BYPOSITION);
            CString csMenuName = szMenuName;

            int iPosChar = csMenuName . Find ('&');    // Remove the '&' character
            if (iPosChar != -1)  csMenuName = csMenuName . Left (iPosChar) + csMenuName . Mid (iPosChar + 1);
            iPosChar = csMenuName . Find ('\t');       // Remove the accelerator key
            if (iPosChar != -1)  csMenuName = csMenuName . Left (iPosChar);

            if (csMenuName . CompareNoCase (a_szParameter [iMenuLevel]) == 0)  break;
        }

        if (i == iNumberOfItem)  // Not found
        {
            if (iMenuLevel == iNumberOfLevel - 1)
            {
                p_stVariablePresent -> csValue = TUT_C_szFalse;
                p_stVariableEnabled -> csValue = TUT_C_szFalse;
                p_stVariableChecked -> csValue = TUT_C_szFalse;
                return TUT_C_iNoError;
            }
            else return TUT_C_iError_UnknownMenuName;
        }

        if (iMenuLevel < iNumberOfLevel - 1)
        {
            hMenu = GetSubMenu (hMenu , i);
            if (hMenu == NULL)  return TUT_C_iError_NotASubMenu;
        }
    }

    // Here, we have found the entry
    MENUITEMINFO stMenuItemInfo;
    stMenuItemInfo . cbSize = sizeof (MENUITEMINFO);
    stMenuItemInfo . fMask  = MIIM_STATE;
    GetMenuItemInfo (hMenu , i , TRUE , & stMenuItemInfo);

    p_stVariablePresent -> csValue = TUT_C_szTrue;
    p_stVariableEnabled -> csValue = stMenuItemInfo . fState & MFS_DISABLED ? TUT_C_szFalse : TUT_C_szTrue;
    p_stVariableChecked -> csValue = stMenuItemInfo . fState & MFS_CHECKED  ? TUT_C_szTrue  : TUT_C_szFalse;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteFree
// Execute a "free" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteFree (char * _szParameter)
{
    // Get parameter : variable name
    int    iNumberOfParameter;
    char * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Find the variable
    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    POSITION Pos = m_oListOfVariable . Find (p_stVariableDef);
    ASSERT (Pos);
    m_oListOfVariable . RemoveAt (Pos);
    delete p_stVariableDef;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetCount
// Execute a "get count" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetCount (char * _szParameter)
{
    // Get parameter : control name and variable name
    int    iNumberOfParameter;
    char * szControlName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szControlName);
    if (! p_stWndDef)  return TUT_C_iError_UnknownControl;

    int iNumberOfElement;
    if (p_stWndDef -> eType == TUT_eListBox)
    {
        HWND hListBox   = p_stWndDef -> hWnd;
        if (! IsWindow        (hListBox))  return TUT_C_iError_InvalidListBoxHandle;
        if (! IsWindowVisible (hListBox))  return TUT_C_iError_InvisibleControl;

        iNumberOfElement = SendMessage (hListBox , LB_GETCOUNT , 0 , 0);
    }
    else
    if (p_stWndDef -> eType == TUT_eComboBox)
    {
        HWND hComboBox  = p_stWndDef -> hWnd;
        if (! IsWindow        (hComboBox))  return TUT_C_iError_InvalidComboBoxHandle;
        if (! IsWindowVisible (hComboBox))  return TUT_C_iError_InvisibleControl;

        iNumberOfElement = SendMessage (hComboBox , CB_GETCOUNT , 0 , 0);
    }
    else
    if (p_stWndDef -> eType == TUT_eListCtrl)
    {
        HWND hListCtrl = p_stWndDef -> hWnd;
        if (! IsWindow         (hListCtrl))  return TUT_C_iError_InvalidListCtrlHandle;
        if (! IsWindowVisible  (hListCtrl))  return TUT_C_iError_InvisibleControl;

        iNumberOfElement = ListView_GetItemCount (hListCtrl);
    }
	else
	if (p_stWndDef -> eType == TUT_eTextEdit && (GetWindowLong (p_stWndDef -> hWnd , GWL_STYLE) & ES_MULTILINE))
	{
        HWND hTextEdit = p_stWndDef -> hWnd;
        if (! IsWindow         (hTextEdit))  return TUT_C_iError_InvalidTextEditHandle;
        if (! IsWindowVisible  (hTextEdit))  return TUT_C_iError_InvisibleControl;

        iNumberOfElement = SendMessage (hTextEdit , EM_GETLINECOUNT , 0 , 0);
	}
    else return TUT_C_iError_CountCtrlRequired;

    m_fn_vSetValueToVariable (p_stVariableDef , iNumberOfElement);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteMessageBox
// Execute a "message box" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteMessageBox (char * _szParameter)
{
    // Get parameter : control name (OK, Cancel...)

    int    iNumberOfParameter;
    char * szControlName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szControlName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int    iControlId = m_fn_iFindIdForMessageBox (szControlName);
    if (! iControlId)  return TUT_C_iError_UnknownMessageBoxCtrl;

    // Give the focus to the main thread
    SetForegroundWindow (AfxGetMainWnd () -> m_hWnd);

    HWND hMessageBox      = GetForegroundWindow ();

    // Count the number of buttons & statics
    int  iNumberOfButtons  = 0;
	int  iNumberOfStatics  = 0;
	int  iNumberOfControls = 0;
	int  iLastStatic	   = 0;
    HWND hControl          = GetWindow (hMessageBox , GW_CHILD);
    while (hControl)
    {
		iNumberOfControls ++;
        if (GetClassLong (hControl , GCW_ATOM) == 0x000C015)    iNumberOfButtons ++;
        if (GetClassLong (hControl , GCW_ATOM) == 0x000C01B)    { iNumberOfStatics ++; iLastStatic = iNumberOfControls; }
        hControl = GetNextWindow (hControl , GW_HWNDNEXT);
    }

	// A MessageBox has 1 or 2 statics and the last control is a static
    if (iNumberOfStatics < 1 || iNumberOfStatics > 2 || iLastStatic != iNumberOfControls)  return TUT_C_iError_UnknownMessageBoxCtrl;

    // If there is only a "OK" button, its ID is IDCANCEL !
    if (iNumberOfButtons == 1 && iControlId == IDOK)  iControlId = IDCANCEL;

    hControl              = GetWindow (hMessageBox , GW_CHILD);
    while (hControl && GetDlgCtrlID (hControl) != iControlId)
    {
        hControl = GetNextWindow (hControl , GW_HWNDNEXT);
    }
    if (! hControl)  return TUT_C_iError_MessageBoxCtrlNotFound;

    m_fn_vMoveMouseCursor (hControl);
    SetForegroundWindow   (hMessageBox);
    m_fn_vClickOnControl  (hControl);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSetResult
// Execute a "set result" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSetResult (char * _szParameter)
{
    // Get parameters : results
    int    iNumberOfParameter;
    char * a_szResult [9];

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 9 , & iNumberOfParameter , a_szResult ,
                                               a_szResult + 1 , a_szResult + 2 , a_szResult + 3 , a_szResult + 4 ,
                                               a_szResult + 5 , a_szResult + 6 , a_szResult + 7 , a_szResult + 8);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    m_iNumberOfResult = iNumberOfParameter;
    for (int i = 0 ; i < m_iNumberOfResult ; i ++)
        m_a_csResult [i] = a_szResult [i];

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetResult
// Execute a "get result" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetResult (char * _szParameter)
{
    if (m_iNumberOfResult == 0)  return TUT_C_iError_NoReturnValue;

    // Get parameters
    int    iNumberOfParameter;
    char * a_szVariableName [9];

    int    iReturnValue = m_fn_iGetParameters (_szParameter , m_iNumberOfResult , m_iNumberOfResult , & iNumberOfParameter , a_szVariableName ,
                                               a_szVariableName + 1 , a_szVariableName + 2 , a_szVariableName + 3 , a_szVariableName + 4 ,
                                               a_szVariableName + 5 , a_szVariableName + 6 , a_szVariableName + 7 , a_szVariableName + 8);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    for (int i = 0 ; i < iNumberOfParameter ; i ++)
    {
        TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (a_szVariableName [i]);
        if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

        p_stVariableDef -> csValue = m_a_csResult [i];
    }

    m_iNumberOfResult = 0;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteKey
// Execute a "key" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteKey (char * _szParameter)
{
    // Get parameters : key code, number of times to press and window which must receive the key
    int    iNumberOfParameter;
    char * szKeyCode , * szNumberOfTime , * szWindowToActivate;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 3 , & iNumberOfParameter ,
                                               & szKeyCode , & szNumberOfTime , & szWindowToActivate);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    BOOL bUseControl = FALSE;
    if (strlen (szKeyCode) > 1 && * szKeyCode == TUT_C_cControl)
    {
        bUseControl = TRUE;
        szKeyCode ++;
    }

    BOOL bUseAlt = FALSE;
    if (strlen (szKeyCode) > 1 && * szKeyCode == TUT_C_cAlt)
    {
        bUseAlt = TRUE;
        szKeyCode ++;
    }

    BOOL bUseShift = FALSE;
    if (strlen (szKeyCode) > 1 && * szKeyCode == TUT_C_cShift)
    {
        bUseShift = TRUE;
        szKeyCode ++;
    }

    int iVirtualKeyCode = m_fn_iFindVirtualKeyCode (szKeyCode);
    if (iVirtualKeyCode == 0)  return TUT_C_iError_UnknownKey;

    int iNumberOfTime;
    if (iNumberOfParameter >= 2)
    {
        iNumberOfTime = m_fn_iConvertToInt (szNumberOfTime);
        if (iNumberOfTime == TUT_C_iErrorNotANumber)  return TUT_C_iError_InvalidNumber;
    }
    else iNumberOfTime = 1;

    HWND hWindowToActivate;
    if (iNumberOfParameter >= 3)
    {
        TUT_tdstWndDefinition * p_stWndDef = m_fnp_stFindWndDefinition (szWindowToActivate);
        if (! p_stWndDef)  return TUT_C_iError_UnknownControl;
        hWindowToActivate= p_stWndDef -> hWnd;
    }
    else hWindowToActivate = AfxGetMainWnd () -> m_hWnd;    // main frame

    SetForegroundWindow (hWindowToActivate);

    // Send all the keys
    if (bUseControl)  m_fn_vSendKey (VK_CONTROL , TRUE);
    if (bUseAlt    )  m_fn_vSendKey (VK_MENU    , TRUE);
    if (bUseShift  )  m_fn_vSendKey (VK_SHIFT   , TRUE);

    for (int i = 1 ; i <= iNumberOfTime ; i ++)
    {
        m_fn_vSendKey (iVirtualKeyCode , TRUE);
    }

    m_fn_vSendKey (iVirtualKeyCode , FALSE);

    if (bUseShift  )  m_fn_vSendKey (VK_SHIFT   , FALSE);
    if (bUseAlt    )  m_fn_vSendKey (VK_MENU    , FALSE);
    if (bUseControl)  m_fn_vSendKey (VK_CONTROL , FALSE);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecutePressOrRelease
// Execute a "press" or a "release" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecutePressOrRelease (char * _szParameter , BOOL _bKeyPressed)
{
    // Get parameter : key code
    int    iNumberOfParameter;
    char * szKeyCode;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szKeyCode);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int iVirtualKeyCode = m_fn_iFindVirtualKeyCode (szKeyCode);
    if (iVirtualKeyCode == 0)  return TUT_C_iError_UnknownKey;

    // Give the focus to the main thread
    SetForegroundWindow (AfxGetMainWnd () -> m_hWnd);

	// update gloval values
	if (iVirtualKeyCode == 0x10)	m_bUseShift		= _bKeyPressed;	// Shift
	if (iVirtualKeyCode == 0x11)	m_bUseControl	= _bKeyPressed;	// Control
	if (iVirtualKeyCode == 0x12)	m_bUseAlt		= _bKeyPressed;	// Alt

    // send the key
    m_fn_vSendKey (iVirtualKeyCode , _bKeyPressed);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSetKeySpeed
// Execute a "set key speed" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSetKeySpeed (char * _szParameter)
{
    // Get parameter : duration
    int    iNumberOfParameter;
    char * szDuration;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szDuration);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int    iDuration = m_fn_iConvertToInt (szDuration);
    if (iDuration == TUT_C_iErrorNotANumber || iDuration < 0)  return TUT_C_iError_InvalidNumber;

    m_iDurationBetweenKeys = iDuration;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIsExisting
// Execute a "is existing" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteIsExisting (char * _szParameter)
{
    // Get parameters : control name and variable name
    int    iNumberOfParameter;
    char * szControlName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szControlName , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szControlName);
    p_stVariableDef -> csValue = p_stWndDef ? TUT_C_szTrue : TUT_C_szFalse;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteGetLength
// Execute a "get length" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetLength (char * _szParameter)
{
    // Get parameters : string and variable name
    int    iNumberOfParameter;
    char * szString , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szString , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    m_fn_vSetValueToVariable (p_stVariableDef , strlen (szString));

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteFindString
// Execute a "find string" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteFindString (char * _szParameter)
{
    // Get parameters : string, sub-string and variable name
    int    iNumberOfParameter;
    char * szString , * szSubString , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 3 , 3 , & iNumberOfParameter ,
                                               & szString , & szSubString , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    CString csString    = szString;
    CString csSubString = szSubString;
    csString    . MakeUpper ();
    csSubString . MakeUpper ();

    int    iPosition = csString . Find (csSubString) + 1;

    m_fn_vSetValueToVariable (p_stVariableDef , iPosition);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteExtractString
// Execute a "extract string" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteExtractString (char * _szParameter)
{
    // Get parameters : string, position of the first character of the string to extrac, length of the string to extract
    // and variable name
    int    iNumberOfParameter;
    char * szString , * szPosition , * szLength , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 3 , 4 , & iNumberOfParameter ,
                                               & szString , & szPosition , & szLength , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    if (iNumberOfParameter == 3)  szVariableName = szLength;    // The optional parameter is Length and not VariableName

    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

    int    iPosition = m_fn_iConvertToInt (szPosition);
    if (iPosition == TUT_C_iErrorNotANumber || iPosition <= 0)  return TUT_C_iError_InvalidNumber;

    CString csString = szString;
    if (iNumberOfParameter == 4)
    {
        int iLength = m_fn_iConvertToInt (szLength);
        if (iLength == TUT_C_iErrorNotANumber || iLength < 0)  return TUT_C_iError_InvalidNumber;
        p_stVariableDef -> csValue = csString . Mid (iPosition - 1 , iLength);
    }
    else p_stVariableDef -> csValue = csString . Mid (iPosition - 1);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteBeep
// Execute a "beep" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteBeep (char * _szParameter)
{
    // Get parameters : frequency and duration
    int    iNumberOfParameter;
    char * szFrequency , * szDuration;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 0 , 2 , & iNumberOfParameter , & szFrequency , & szDuration);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    int    iFrequency;
    if (iNumberOfParameter >= 1)
    {
        iFrequency= m_fn_iConvertToInt (szFrequency);
        if (iFrequency == TUT_C_iErrorNotANumber || iFrequency <= 0)  return TUT_C_iError_InvalidNumber;
    }
    else iFrequency = 440;

    int    iDuration;
    if (iNumberOfParameter >= 2)
    {
        iDuration = m_fn_iConvertToInt (szDuration);
        if (iDuration  == TUT_C_iErrorNotANumber || iDuration  <= 0)  return TUT_C_iError_InvalidNumber;
    }
    else iDuration = 200;

    Beep (iFrequency , iDuration);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecutePlay
// Execute a "play" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecutePlay (char * _szParameter)
{
    m_fn_vSetGetLastError (TUT_C_iNoError);

    // Get parameter : name if the .wav file
    int    iNumberOfParameter;
    char * szFileName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szFileName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    BOOL bOk = PlaySound (szFileName , NULL , SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
    if (!bOk)  m_fn_vSetGetLastError (TUT_C_iError_SoundError);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIsSelected
// Execute a "is selected" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteIsSelected (char * _szParameter)
{
    m_fn_vSetGetLastError (TUT_C_iNoError);

    // Get parameters : list box name, text to test and variable name
    int    iNumberOfParameter;
    char * szListBoxName , * szTextToTest , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 3 , 3 , & iNumberOfParameter ,
                                               & szListBoxName , & szTextToTest , & szVariableName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Get the list box and test it
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition  (szListBoxName);
    if (! p_stWndDefinition)                         return TUT_C_iError_UnknownListBox;

	if (p_stWndDefinition -> eType == TUT_eListBox)
	{
		HWND hListBox = p_stWndDefinition -> hWnd;
		if (! IsWindow        (hListBox))  return TUT_C_iError_InvalidListBoxHandle;
		if (! IsWindowVisible (hListBox))  return TUT_C_iError_InvisibleControl;

		// Find or create the variable
		TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
		if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

		// Find the text to test
		int iLineNumber = SendMessage (hListBox , LB_FINDSTRINGEXACT , -1 , (LPARAM) szTextToTest);
		if (iLineNumber == LB_ERR)
		{
			m_fn_vSetGetLastError (TUT_C_iError_ListBoxItemNotFound);
			p_stVariableDef -> csValue = TUT_C_szFalse;
			return TUT_C_iNoError;
		}

		BOOL bIsSelected = FALSE;

		if ((GetWindowLong (hListBox , GWL_STYLE) & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) == 0)
		{
			// single selection
			bIsSelected = (((int)SendMessage (hListBox , LB_GETCURSEL , 0 , 0)) == iLineNumber); 
		}
		else
		{
			// multiple selection
			bIsSelected = SendMessage (hListBox , LB_GETSEL , iLineNumber , 0);
		}

		p_stVariableDef -> csValue = bIsSelected ? TUT_C_szTrue : TUT_C_szFalse;
	}
	else if (p_stWndDefinition -> eType == TUT_eListCtrl)
	{
        HWND hListCtrl = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hListCtrl))  return TUT_C_iError_InvalidListCtrlHandle;
        if (! IsWindowVisible (hListCtrl))  return TUT_C_iError_InvisibleControl;

		// Find or create the variable
		TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
		if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

        LV_FINDINFO stFindInfo;
        stFindInfo . flags = LVFI_STRING;
        stFindInfo . psz   = szTextToTest;
        
		int iLineNumber = ListView_FindItem (hListCtrl , -1 , & stFindInfo);        
		if (iLineNumber == -1)
        {
            m_fn_vSetGetLastError (TUT_C_iError_ListCtrlItemNotFound);
            return TUT_C_iNoError;
        }

        int iResult = ListView_GetItemState (hListCtrl , iLineNumber , LVIS_SELECTED );

		p_stVariableDef -> csValue = (iResult & LVIS_SELECTED) ? TUT_C_szTrue : TUT_C_szFalse;
	}
	else
		return TUT_C_iError_ListBoxRequired;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteVar
// Execute a "var" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteVar (char * _szParameter)
{
    // Get parameters : variable name and value
    int    iNumberOfParameter;
    char * szVariableName , * szValue;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 2 , & iNumberOfParameter , & szVariableName , & szValue);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Check if there is already a local variable with the same name
    TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
    if (p_stVariableDef && p_stVariableDef -> iContextNumber != 0)  return TUT_C_iError_LocalVariableExist;

    // Create the variable
    p_stVariableDef = new TUT_tdstVariableDefinition;
    p_stVariableDef -> csName         = szVariableName;
    p_stVariableDef -> csValue        = szValue;
    p_stVariableDef -> iContextNumber = m_oListOfContext . GetCount ();

    m_oListOfVariable . AddTail (p_stVariableDef);

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIsSelected
// Execute a "global var" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGlobalVar (char * _szParameter)
{
    // Get parameters : variable name and value
    int    iNumberOfParameter;
    char * szVariableName , * szValue;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 2 , & iNumberOfParameter , & szVariableName , & szValue);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Check if there is already a global variable with the same name
	TUT_tdstVariableDefinition * p_stVariableDef = NULL;
    POSITION Pos = m_oListOfVariable . GetHeadPosition ();
    while (Pos)
    {
        TUT_tdstVariableDefinition * p_stVarDef = m_oListOfVariable . GetNext (Pos);
        if (p_stVarDef -> csName . CompareNoCase (szVariableName) == 0 && p_stVarDef -> iContextNumber == 0)
		{
			p_stVariableDef = p_stVarDef;
			break;
		}
    }

    // Create the variable
	if( p_stVariableDef == NULL )
	{
		p_stVariableDef = new TUT_tdstVariableDefinition;
		m_oListOfVariable . AddTail (p_stVariableDef);
	}

	ASSERT( p_stVariableDef );

    p_stVariableDef -> csName         = szVariableName;
    p_stVariableDef -> csValue        = iNumberOfParameter >= 2 ? szValue : "";
    p_stVariableDef -> iContextNumber = 0;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteIsInList
// Execute a "global var" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteIsInList (char * _szParameter)
{
    m_fn_vSetGetLastError (TUT_C_iNoError);

    // Get parameters : variable name and value
    int    iNumberOfParameter;
    char * szControlName , * szTextToFind , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 3 , 3 , & iNumberOfParameter ,
											   & szControlName , & szTextToFind , & szVariableName);

    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

    // Get the list box and test it
    TUT_tdstWndDefinition * p_stWndDefinition = m_fnp_stFindWndDefinition  (szControlName);
    if (! p_stWndDefinition)                         return TUT_C_iError_UnknownListBox;

	if (p_stWndDefinition -> eType == TUT_eListBox)
	{
		HWND hListBox = p_stWndDefinition -> hWnd;
		if (! IsWindow        (hListBox))  return TUT_C_iError_InvalidListBoxHandle;
		if (! IsWindowVisible (hListBox))  return TUT_C_iError_InvisibleControl;

		// Find or create the variable
		TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
		if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

		// Find the text to test
		int iLineNumber = SendMessage (hListBox , LB_FINDSTRINGEXACT , -1 , (LPARAM) szTextToFind);

		p_stVariableDef -> csValue = (iLineNumber != LB_ERR) ? TUT_C_szTrue : TUT_C_szFalse;
	}
	else if (p_stWndDefinition -> eType == TUT_eListCtrl)
	{
        HWND hListCtrl = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hListCtrl))  return TUT_C_iError_InvalidListCtrlHandle;
        if (! IsWindowVisible (hListCtrl))  return TUT_C_iError_InvisibleControl;

		// Find or create the variable
		TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
		if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

        LV_FINDINFO stFindInfo;
        stFindInfo . flags = LVFI_STRING;
        stFindInfo . psz   = szTextToFind;
        
		int iLineNumber = ListView_FindItem (hListCtrl , -1 , & stFindInfo);        

		p_stVariableDef -> csValue = (iLineNumber != -1) ? TUT_C_szTrue : TUT_C_szFalse;
	}
	else if (p_stWndDefinition -> eType == TUT_eComboBox)
	{
        HWND hComboBox = p_stWndDefinition -> hWnd;
        if (! IsWindow        (hComboBox))  return TUT_C_iError_InvalidComboBoxHandle;
        if (! IsWindowVisible (hComboBox))  return TUT_C_iError_InvisibleControl;

		// Find or create the variable
		TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
		if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

        int iLineNumber = SendMessage (hComboBox , CB_FINDSTRINGEXACT , -1 , (LPARAM) szTextToFind);

        p_stVariableDef -> csValue = (iLineNumber != CB_ERR) ? TUT_C_szTrue : TUT_C_szFalse;
	}
	else
		return TUT_C_iError_SelectCtrlRequired;

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSaveContext
// Execute a "SaveContext" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSaveContext(char * _szParameter)
{
    // Get parameter : name of the log file
    int    iNumberOfParameter;
    char * szLogFileName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szLogFileName);
    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

	// open file
	FILE *pFile = fopen (szLogFileName , "wt");
	if (pFile)
	{
		// save global variables
		POSITION Pos = m_oListOfVariable . GetHeadPosition ();
		while (Pos)
		{
			TUT_tdstVariableDefinition * p_stVariableDef = m_oListOfVariable . GetNext (Pos);

			if (p_stVariableDef -> iContextNumber == 0)	// global variable
			{
				char szLine[255];

				strcpy (szLine , TUT_C_szGlobalVar								);
				strcat (szLine , " ( "											);
				strcat (szLine , (char*) (LPCTSTR) p_stVariableDef -> csName	);
				strcat (szLine , " , \""										);
				strcat (szLine , (char*) (LPCTSTR) p_stVariableDef -> csValue	);
				strcat (szLine , "\" )"											);
				//
				fprintf (pFile , "%s\n" , szLine);
			}
		}
		// close file
		fclose( pFile );
	}

    return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteRestoreContext
// Execute a "RestoreContext" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteRestoreContext(char * _szParameter)
{
    // Get parameter : name of the log file
    int    iNumberOfParameter;
    char * szLogFileName , * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter ,
																	  & szLogFileName, & szVariableName);

    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

	// Find or create the variable
	TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
	if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

	// open file
	if (_access (szLogFileName , 00) == 0)
	{
		p_stVariableDef -> csValue = TUT_C_szTrue;
		return m_fn_iExecuteCall (_szParameter);
	}
	else
	{
		p_stVariableDef -> csValue = TUT_C_szFalse;
		return TUT_C_iNoError;
	}
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_ExecuteGetFocusWindow
// Execute a "Get Focus Window" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteGetFocusWindow(char * _szParameter)
{
    m_fn_vSetGetLastError (TUT_C_iNoError);

    // Get parameter : name of the return variable
    int    iNumberOfParameter;
    char * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 1 , 1 , & iNumberOfParameter , & szVariableName);

    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

	// Find or create the variable
	TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
	if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;


	// Get foreGroundWindow
	HWND					hWnd				= GetForegroundWindow();
	TUT_tdstWndDefinition * p_stWndDefinition	= hWnd ? m_fnp_stFindWndDefinition (hWnd) : NULL;

	if (p_stWndDefinition)
	{
		p_stVariableDef -> csValue = p_stWndDefinition -> csName;
	}
	else
	{
		p_stVariableDef -> csValue = "";
		m_fn_vSetGetLastError (TUT_C_iError_UnregisteredWindow);
	}
	return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_ExecuteHasFocus
// Execute a "Has Focus" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteHasFocus(char * _szParameter)
{
    // Get parameter : name of the return variable
    int    iNumberOfParameter;
    char * szWindowName, * szVariableName;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 2 , 2 , & iNumberOfParameter , & szWindowName , & szVariableName);

    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

	// Find or create the variable
	TUT_tdstVariableDefinition * p_stVariableDef = m_fnp_stFindVariable (szVariableName);
	if (p_stVariableDef == NULL)  return TUT_C_iError_UnknownVariable;

	// search window
    TUT_tdstWndDefinition      * p_stWndDef      = m_fnp_stFindWndDefinition    (szWindowName);
    if (! p_stWndDef)  return TUT_C_iError_UnknownControl;

	// Get foreGroundWindow
	HWND					hWnd				= GetForegroundWindow();
	TUT_tdstWndDefinition * p_stWndDefinition	= hWnd ? m_fnp_stFindWndDefinition (hWnd) : NULL;

	if (p_stWndDefinition)
	{
		p_stVariableDef -> csValue = (p_stWndDefinition -> csName . CompareNoCase( p_stWndDef -> csName )) ? TUT_C_szFalse : TUT_C_szTrue ;
	}
	else
	{
		p_stVariableDef -> csValue = TUT_C_szFalse;
	}
	return TUT_C_iNoError;
}

//-----------------------------------------------------------------------------
// class TUT_CTutorialManager - m_fn_iExecuteSelectText
// Execute a "Select Text" tutorial script action
// Return 0 if no error
//-----------------------------------------------------------------------------
int TUT_CTutorialManager::m_fn_iExecuteSelectText(char * _szParameter)
{
    // Get parameter : name of the return variable
    int    iNumberOfParameter;
    char * szWindowName, * szFirstLine, * szFirstChar, * szLastLine, * szLastChar;

    int    iReturnValue = m_fn_iGetParameters (_szParameter , 5 , 5 , & iNumberOfParameter , & szWindowName , 
																	  & szFirstLine        , & szFirstChar,
																	  & szLastLine         , & szLastChar       );

    if (iReturnValue != TUT_C_iNoError)  return iReturnValue;

	// search window
    TUT_tdstWndDefinition  * p_stWndDefinition  = m_fnp_stFindWndDefinition    (szWindowName);

    if (! p_stWndDefinition)  return TUT_C_iError_UnknownControl;
    if (p_stWndDefinition -> eType != TUT_eTextEdit)  return TUT_C_iError_TextEditRequired;

    HWND hTextEdit = p_stWndDefinition -> hWnd;
    if (! IsWindow        (hTextEdit))  return TUT_C_iError_InvalidTextEditHandle;
    if (! IsWindowVisible (hTextEdit))  return TUT_C_iError_InvisibleControl;
    if (! IsWindowEnabled (hTextEdit))  return TUT_C_iError_DisabledControl;

    int iFirstLine = m_fn_iConvertToInt (szFirstLine);
    int iFirstChar = m_fn_iConvertToInt (szFirstChar);
    int iLastLine  = m_fn_iConvertToInt (szLastLine);
    int iLastChar  = m_fn_iConvertToInt (szLastChar);
	int iNbLines   = SendMessage (hTextEdit , EM_GETLINECOUNT , 0 , 0);

	BOOL bMultiLine = (GetWindowLong (hTextEdit , GWL_STYLE) & ES_MULTILINE) != 0;

	if (iLastLine == 0)		iLastLine = iNbLines;

	if (bMultiLine)
	{
		if (iFirstChar <=0 || (iFirstLine > iLastLine) || ((iFirstLine == iLastLine) && (iFirstChar > iLastChar)) )
			return TUT_C_iError_UncoherentSelectionLimits;
	}
	else
	{
		if (iFirstLine != 1 || iFirstLine != 1 || iFirstChar <= 0)
			return TUT_C_iError_UncoherentSelectionLimits;
	}

	int iBegin, iEnd;

	if( bMultiLine )
	{
		// select on multiline edit
		iBegin     = (iFirstLine > 0 ? SendMessage (hTextEdit , EM_LINEINDEX , (WPARAM)iFirstLine-1 , 0) : 0 ) + iFirstChar - 1;
		if (iLastLine && (iLastLine <= iNbLines) )
			iEnd   = iFirstChar ? SendMessage (hTextEdit , EM_LINEINDEX , (WPARAM)iLastLine -1 , 0) + iFirstChar - 1
				                : SendMessage (hTextEdit , EM_LINEINDEX , (WPARAM)iLastLine    , 0);
		else
			iEnd   = GetWindowTextLength (hTextEdit);
	}
	else
	{
		// select on single line edit
		iBegin     = iFirstChar - 1;
		iEnd       = (iLastChar ? iLastChar : GetWindowTextLength (hTextEdit) );
	}

	PostMessage (hTextEdit, EM_SETSEL, (WPARAM) iBegin, (LPARAM) iEnd );

	return TUT_C_iNoError;
}