/*********************************** CDevilCrucible.cpp ***************************
 *                                                                                *
 *   #include "CDevilCrucible.h"                                                  *
 *   Project: MedinTux                                                            *
 *   Copyright (C) 2003 2004 2005 by Sevin Roland  and Data Medical Design        *
 *   E-Mail: data.medical.design@club-internet.fr                                 *
 *   Eric MAEKER 2007 2008 eric.maeker@free.fr                                    *
 *                                                                                *
 *   This program is free software; you can redistribute it and/or modify         *
 *   it under the terms of the CeCILL Version 1 du 21/06/2004                     *
 *   License (GPL compatible)                                                     *
 *                              http://www.cecill.info/                           *
 *   as published by :                                                            *
 *                                                                                *
 *   Commissariat à l'Energie Atomique                                            *
 *   - CEA,                                                                       *
 *                            31-33 rue de la Fédération, 75752 PARIS cedex 15.   *
 *                            FRANCE                                              *
 *   Centre National de la Recherche Scientifique                                 *
 *   - CNRS,                                                                      *
 *                            3 rue Michel-Ange, 75794 Paris cedex 16.            *
 *                            FRANCE                                              *
 *   Institut National de Recherche en Informatique et en Automatique             *
 *   - INRIA,                                                                     *
 *                            Domaine de Voluceau, Rocquencourt, BP 105, 78153    *
 *                            FRANCE                                              *
 *                                                                                *
 *         This program is distributed in the hope that it will be useful,        *
 *         but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   *
 *         CeCILL  License (GPL compatible) for more details.                     *
 *                                                                                *
 **********************************************************************************/

/*! \class CDevilCrucible
 *  \brief Classe de script de MedinTux.
 * DrTux possède un langage de script permettant d'accéder à diverses fonctions et informations du dossier patient.
 * L'appel aux éléments de script se fait dans les masques de documents. Les instructions de script doivent être délimitées par des accolade {{ INSTRUCTION }}. Le langage de script de DrTux est récursif et permet l'utilisation d'élément de script à l'intérieur même d'une autre instruction de script. Exemple :
 * {{:: Calc ( 5, *, {{POIDS PATIENT}} ) }} -> calculera le poids multiplié par 5.
 * Le texte de script est directement remplacé par le résultat de l'instruction exécutée. Dans notre exemple, le script sera exécuté en deux fois.
 * 1 = {{:: Calc ( 5, *, 30 ) }}  puis
 * 2 = 150
 * Ce langage de script permet de créer des formulaires dynamique, des protocoles de soins...
 *
 * Divers éléments sont accessibles :
 * - les informations concernant le patient (items),
 * - les informations concernant le praticien (items),
 * - de nombreuses fonctions d'extraction de données, de calcul, de tri... (fonctions)
 *
 * Cette classe une fois instanciée correctement donne la possibilité d'interpréter le script placé dans le document passé en paramètre. Pour interpréter le script il faut appeler DoMixture.
 *
 * La fonction GetResolvTokenValue() remplace un item de script (un item est non précédé par les deux fois deux points '::' au sein des accolades) par son interprétation. Les fonctions sont explosées selon la première lettre de l'item de script. Les fonctions se nomment exeItemsFrom_A_To_D(), exeItemsFrom_E_To_H(), exeItemsFrom_I_To_L(), exeItemsFrom_M_To_P(), exeItemsFrom_Q_To_T(), exeItemsFrom_U_To_X(), exeItemsFrom_Y_To_Z(). Une tableau de pointeur de fonction (ItemDispatcher pour les items de script et FunctionDispatcher pour les fonctions de script) défini dans le constructeur de la classe est chargé de l'aiguillage.
 *
 *
 * <b>Fonctions générales de la classe :</b>
 * - DoMixture() : recherche le script (token) dans le document et le remplace par son résultat (resolvToken)
 * - exeFunction() : analyse le texte à la recherche de scripts d'extraction complexes : extract, extract_traitement, extract_table
 * - extractArgList() : récupère les arguments passés en paramètre de l'item de script
 * - GetDateOfActiveRubrique() : retourne la date du document sur lequel le travail est effectué
 * - GetIDCurrentDoc()
 * - GetIdCurrentFamilyDoc()
 * - findLastDelimiter() : trouve dans le texte l'accolade de fermeture
 * - GetMB() : retourne le pointeur de CMoteurBase en cours d'utilisation dans la classe (utilisé par CDevilGraph)
 * - GetMIdentPrimKey() : retourne m_IdentPrimKey (utilisé par CDevilGraph)
 * - GetUserDocPk() : retourne m_UserdocPk (utilisé par CDevilGraph)
 * - GetResolvTokenValue() : interprète l'intégralité du document passé en paramètre
 * - ReplaceTokenByValue() : remplace l'élément de script par son résultat
 * - ResolvScriptTokenSyntax() : 
 * - ResolvTokenSyntax() :
 * - HtmlFind() : saute les éléments HTML lors de l'interprétation du script
 * - IfHtmlTagJumpIt() : saute les éléments HTML lors de l'interprétation du script
 * - IdentValidCarToFieldName() :
 * - NormaliseDate() : transforme une date en Qt::ISODate


 * <b>Fonctions d'extraction de la base de données patient :</b> 
 * - Func_Extract_ATCD() : extrait les antécédent du terrain en cours d'affichage (celui de Atcd_Code)
 * - Func_Extract() : extrait et insère du texte du texte lui même en partant d'un délimiteur de début et un délimiteur de fin
 * - Func_Extract_Table() : extrait et insère les variables du terrain en cours
 * - Func_Extract_TTT() : extrait et insère le traitement en cours
 * - Func_Extract_VAR() : extrait et insère des éléments des variables du patient
 * - GetPoidsTaille() : A recoder
 * - GetVarNeerer() (appelé à devenir obsolete) récupère une variable à proximité d'une date
 * - GetVar() : récupère les valeurs d'une variable du terrain
 * - Terme_Obst() : calcul le terme obstétrical à la date du jour.
 *
 * <b>Fonctions d'extraction de la base de données praticien :</b> 
 * - DoFusionIntervenantStruct()
 *
 * <b>Fonctions de calcul :</b>
 * - GetDDR() récupère la date des dernières règles,
 * - daysTo() calcul le nombre de jour entre deux dates
 * - 
 * <b>Fonctions graphiques :</b> 
 * <b>Fonctions de gestion des documents :</b> 
 * - DocNameToDocNumType() transforme le type de document ("Observation","Prescription"...) en son type numérique,
 *
 * \sa DrTux::FusionneDocument
 *
 * \todo utilisation de la classe (toujours synchronisée avec les éléments affichés) Atcd_Code.
 */

#include "CDevilCrucible.h"
#include "CApp.h"
#include "../../MedinTuxTools/CHtmlTools.h"
#include "MyEditText.h"
#include "CMDI_Generic.h"
#include "CMDI_Documents.h"

#include "drtux.h"
#include <qprocess.h>
#include <qmessagebox.h>
#include <qdir.h>
#include <qprocess.h>
#include <qmime.h> 
#include <qpixmap.h> 
#include <qimage.h> 
#include <qeventloop.h> 
#include <qthread.h> 
#include <qfiledialog.h>

#ifdef Q_WS_WIN
   #define F_EXE ".exe"
   #include <windows.h>
#else
   #define F_EXE ""
#endif

#define TR QObject::tr

//----------------------------  FusionneDocument ---------------------------------------------
/*! \brief  Constructeur de la classe. Appelé par drtux::FusionneDocument.
 * \todo Utiliser Atcd_Code, Var_Code
*/
CDevilCrucible::CDevilCrucible(QString                *pDocument     ,       // texte du document à fusionner
                               CMoteurBase            *pCMoteurBase  ,       // moteur de base de données (faut bien accéder aux fonctions)
                               CMedicaBase            *pCMedicaBase  ,       // moteur de base de données medicamenteuses(faut bien accéder aux fonctions)
                               RUBREC_LIST            *pRubList      ,       // liste des documents composant le dossier patient
                               QString                &identPrimKey  ,       // il faut les renseignements sur le patient
                               DOCUMENT_DISPLAY_MAP   *pCurDisplayDocMap,    // liste des rubriques courantes (affichées)
                               QString                &userActuelPk  ,       // utilisateur courant du programme
                               CRubRecord             *pCRubCurrentRecord    // pointeur sur la rubrique en cours de modif (si c'est une rubrique) zero sinon
                              )
{m_pCurDisplayDocMap  = pCurDisplayDocMap;
 m_pRubList           = pRubList;
 m_pMB                = pCMoteurBase;
 m_pMD                = pCMedicaBase;
 m_IdentPrimKey       = identPrimKey;
 m_pDocument          = pDocument;
 m_UserActuelPk       = userActuelPk;
 m_UserDocPk          = userActuelPk;
 m_pCRubCurrentRecord = pCRubCurrentRecord;
 CRubRecord  *pCRubRecord                = GetIDCurrentDoc(TR("Observation"));
 if (pCRubRecord) m_UserDocPk            = m_pMB->GetUserPrimKey(pCRubRecord->m_SignUser);
 // Définit les pointeurs de fonctions
 ItemDispatcher[0] = &CDevilCrucible::exeItemsFrom_A_To_D;
 ItemDispatcher[1] = &CDevilCrucible::exeItemsFrom_E_To_H;
 ItemDispatcher[2] = &CDevilCrucible::exeItemsFrom_I_To_L;
 ItemDispatcher[3] = &CDevilCrucible::exeItemsFrom_M_To_P;
 ItemDispatcher[4] = &CDevilCrucible::exeItemsFrom_Q_To_T;
 ItemDispatcher[5] = &CDevilCrucible::exeItemsFrom_U_To_X;
 ItemDispatcher[6] = &CDevilCrucible::exeItemsFrom_Y_To_Z;

 FunctionDispatcher[0] = &CDevilCrucible::exeFunctionsFrom_A_To_D;
 FunctionDispatcher[1] = &CDevilCrucible::exeFunctionsFrom_E_To_H;
 FunctionDispatcher[2] = &CDevilCrucible::exeFunctionsFrom_I_To_L;
 FunctionDispatcher[3] = &CDevilCrucible::exeFunctionsFrom_M_To_P;
 FunctionDispatcher[4] = &CDevilCrucible::exeFunctionsFrom_Q_To_T;
 FunctionDispatcher[5] = &CDevilCrucible::exeFunctionsFrom_U_To_X;
 FunctionDispatcher[6] = &CDevilCrucible::exeFunctionsFrom_Y_To_Z;
}

/*! \brief destructeur de la classe */
CDevilCrucible::~CDevilCrucible()
{ int i = 0;
  for (i=0; i<7;++i)
  { FunctionDispatcher[i] = 0;
    ItemDispatcher[i] = 0;
  }
}


//------------------------------------------ GetDDR -----------------------------------------------------------------
/*! \brief Récupère dans le terrain la date des dernières règles en cas de grossesse. Retourne la date au format QT::ISODate ou QString(" ") si n'est pas trouvé.
 * \todo utiliser Atcd_Code
*/
QString CDevilCrucible::GetDDR()
{ QString tmp1;
  // recuperer données soit dans liste cache soit sur disque
   QString   str_data    = m_pMB->GetDataFromRubList( GetIDCurrentDoc(TR("Terrain")) );
   if (str_data.length()==0)              return QString(" ");
   char *pt = (char*) (const char*) str_data;

   //........... effacer les anciennes tables de variables ................
   QString   section   = "";
   QString   var_name   = "";

   //char     *prop_section = 0;       // permet de noter le pointeur sur la section propriete renouvelable/intercurent des produits

   while((pt = m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
    {//.................................. Obstétrique ........................................................;
     if (section == TR("Obstétrique"))// && m_IsFeminin == 1)                  // à ce stade pt pointe au debut de la nouvelle ligne
        {
         while (*pt && *pt != '[')
               {pt = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &tmp1); // 18-03-2004
                if (var_name == TR("Grossesse") && tmp1 != TR("non"))   // val1 contient non ou ddr sauvegardée
                   {    tmp1 = tmp1.mid(6) + "-" + tmp1.mid(3,2) + "-" + tmp1.left(2);
                        return tmp1;
                   }
               }// Fin while [
         section="";
        } // Fin if section
    } // Fin while Section

  return QString(" ");

}

//------------------------------------------ Terme_Obst -------------------------------------------------------------
/*! \brief Calcul le terme de la grossesse à la date souhaitée.
 *  \param QDate DDR       Date des dernières règles
 *  \param QDate dt        Date pour le calcul
 *  \return Le terme au format string du style : " XX SA XX Jours "
 *  \todo à déplacer dans Atcd_Code.
*/
QString CDevilCrucible::Terme_Obst(QDate DDR , QDate dt)
{ int nbDays = DDR.daysTo(dt);
  int nbDaysLeft = nbDays % 7;
  int nbWeeks = (nbDays - nbDaysLeft ) / 7;
  QString ret;

  if (dt < DDR) { ret = TR("Le terme n'est pas calculable (terme négatif). Vérifiez vos dates."); }
  else if (nbWeeks > 45) { ret = TR("Le terme n'est pas calculable (> 45 SA). Vérifiez vos dates."); }
  else { ret = QString::number(nbWeeks) + " SA "+ QString::number(nbDaysLeft) + TR(" Jours "); }

  return ret;
}


//------------------------------------------ Terme_Obst -----------------------------------------
/*! \brief Calcul le terme de la grossesse à la date souhaitée.
 *  \param QString DDR     Date des dernières règles format ISODate
 *  \param QDate dt        Date pour le calcul
 *  \return Le terme au format string du style : " XX SA XX Jours "
 */
QString CDevilCrucible::Terme_Obst(QString DDR , QDate dt)
{ if (DDR == "" || DDR == " ") return " ";
  QDate DDR_dt = DDR_dt.fromString ( DDR , Qt::ISODate);

  return Terme_Obst(DDR_dt , dt)        ;
}


//----------------------------------------  DoMixture -------------------------------------------------
/*! \brief Fusionne les champs de fusion un document donnée en entrée par: QString &document cette fonction demande un contexte sur l'observation en cours afin de tenir compte de l'utilisateur ayant créé cette observation ainsi que des elements propres à l'observation: elements du contenu etc....
 * \return QString:  qui est l'utilisateur lié à ce document. En effet l'utilisateur du document peut etre différent de l'utilisateur en cours. Exemple je travaille sur ce patient, et j'edite un certificat créé auparavant sous un autre utilisateur.
 * NOTE:  la date de visite, et l'utilisateur createur du document à fusionner doivent etre imperativement à jour et sont recupérés dans: (*pRubList->at(idDoc)).m_Date et (*pRubList->at(idDoc)).m_User
 */
void CDevilCrucible::DoMixture()
{ DoMixture(0, m_pDocument->length());
}

//----------------------------------  DoMixture -------------------------------------------------
/*! \brief Idem que supra, mais en débutant au caractère pos_deb jusqu'au caractère pos_fin du texte.
 * \return QString:  qui est l'utilisateur lié à ce document. En effet l'utilisateur du document peut etre différent de l'utilisateur en cours. Exemple je travaille sur ce patient, et j'edite un certificat créé auparavant sous un autre utilisateur.
 * NOTE:  la date de visite, et l'utilisateur createur du document à fusionner doivent etre imperativement à jour et sont recupérés dans: (*pRubList->at(idDoc)).m_Date et (*pRubList->at(idDoc)).m_User
 */
int CDevilCrucible::DoMixture(int pos_deb, int pos_max)
{int isWithToken   =  0;
 int pos_end       =  0;
 pos_deb           =  m_pDocument->find("{{", pos_deb);
 int pDeltaLen     =  0;
 int intialLen     =  m_pDocument->length();
 while (pos_deb != -1 && pos_deb < pos_max)
       {pos_end        =  CGestIni::findFermant(m_pDocument, pos_deb+2, pos_max, "{{", "}}", isWithToken);
        if (isWithToken)
           {pos_max   += DoMixture(pos_deb+2, pos_end);
           }
        else
           {ReplaceTokenByValue(*m_pDocument, pos_deb+2, pos_end - pos_deb-2 ,&pDeltaLen);
            pos_max  += pDeltaLen;
            pos_deb   =  m_pDocument->find("{{", pos_deb+2);
           }
       }
  return m_pDocument->length() - intialLen;
}

//----------------- ReplaceTokenByValue --------------------------------------------------
/*! \brief Lorsque le code a été trouvé et interprété, remplace dans le texte le code par son résultat.
 *  \param document : QString du texte entier
 *  \param pos_deb : caractère de départ du remplacement
 *  \param len : longueur
 *  \param pDeltaLen ??
*/
int CDevilCrucible:: ReplaceTokenByValue(QString &document, int pos_deb, int len , int *pDeltaLen /*=0*/)
{int originalLen = document.length();
 QString  resolvToken = GetResolvTokenValue(document, pos_deb,  len )  ;
 resolvToken.replace("SautDeLigne", "<br />");
 document.replace ( pos_deb-2, len+4, resolvToken);
 if (pDeltaLen) *pDeltaLen = document.length() - originalLen;
 return pos_deb-2+resolvToken.length();
}

//------------------------------ GetResolvTokenValue ---------------------------------------
/*! \brief Analyse le texte scripté passé en paramètre et "compile" avec les données calculées.
 *  \return les données calculées
 *  \todo Morceller la recherche du token : le premier caractère appartient à A-M ou M-Z pour accélérer le processus.
*/
QString CDevilCrucible::GetResolvTokenValue(QString &document, int pos_deb, int len )
{char insecSpace[2]={0xa0,0x00};
 QString  token = document.mid(pos_deb, len);
          token.replace((char)0xa0," ");
          token.replace("&gt;",">");
          token.replace("&lt;","<");
          token.replace("&amp;","&");
          token.replace("&nbsp;",insecSpace);

 QString  resolvToken(" ");
// QString  tokenUpper = token.upper();
 QVariant qvar;
 int         f;
 if (token.left(2)=="::")
    {ResolvScriptTokenSyntax(resolvToken, token.mid(2) );
    }
 else if (token[0]=='[')
    {ResolvTokenSyntax(resolvToken, token);
    }
 else
 { // D'abord tester si contient '*' (la fonction calc n'est pas accessible ici car
   // C'est une fonction {{:: calc }} ici nous testons les items {{XXXXXXX}}
   if ((f=token.find('*'))!=-1)   // syntaxe {{ *20}}  ou {{\t*20}}
   {int        nb = atoi(token.mid(f+1));
    QString motif = token.left(f);
    if (motif    != "" )
       {if (motif[0]=='\\')  // echapement exple "\t"
           {
            char escape_car = motif.at(1).latin1();
            switch(escape_car)
              {case 't':
                    resolvToken.fill('\t', nb);
                    break;
               case 'n':
                    resolvToken.fill('\n', nb);
                    break;
               case '\\':
                    resolvToken.fill('\\', nb);
                    break;
               case 'r':
                    resolvToken.fill('\r', nb);
                    break;
               default:
                    resolvToken.fill(motif[1], nb);
                    break;
              } // endswitch(token[1])
           } //endif (token[0]=='\\')  // echapement
        else
           {resolvToken.fill(motif[0], nb);
           }
       }// endif (motif != "" )
   }
   else
   {
     QChar pt = token[0];
     int i = pt.upper().unicode();
     i = i - 65; // A == 0
     i = i / 4;  // de 4 en 4 dans l'alphabet
     if ((i >= 0) && (i < 7)) // reste dans le tableau sinon erreur
     { (this->*(ItemDispatcher[i]))(resolvToken, token, document); 
     }

   }
  }
 return resolvToken;
}


//------------------------------ exeItemsFrom_A_To_D ---------------------------------------
/*! \brief Interprète les items de scripts débutant par la lettre A à D */
void CDevilCrucible::exeItemsFrom_A_To_D(QString &resolvToken, QString token, QString &/*document*/)
{ QString tokenUpper = token.upper();
  QVariant qvar;
 if (tokenUpper[0] == 'A')
 { 
   if ( tokenUpper == "ADRESSE COMPLETE MEDECIN")
    {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_RUE, m_pMB->m_USER_IDENT_PRIMKEY ,       m_UserDocPk)  + "\n";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_CODE_POST, m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk)  + " ";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_VILLE, m_pMB->m_USER_IDENT_PRIMKEY ,     m_UserDocPk) ;
     resolvToken.replace("\n","<br />");
    }
   else if ( tokenUpper == "ADRESSE MEDECIN")
    {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_RUE, m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
     resolvToken.replace("\n","<br />");
    }
   else if (tokenUpper == "AGE PATIENT ABREGE")
    {m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_NSS, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey, &qvar);
     QDate qDteNss = qvar.toDate();
     resolvToken   = m_pMB->CalculeDiffAge(&qDteNss);
    }
   else if (tokenUpper == "AGE PATIENT ANNEE ABREGE")
    {m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_NSS, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey, &qvar);
     QDate qDteNss = qvar.toDate();
     resolvToken   = m_pMB->CalculeDiffAge(&qDteNss);
     resolvToken   = resolvToken.left(resolvToken.length()-5);
    }
   else if (tokenUpper == "AGE PATIENT")
    {m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_NSS, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey, &qvar);
     QDate qDteNss = qvar.toDate();
     resolvToken   = m_pMB->CalculeDiffAge(&qDteNss);
    }
   else if (tokenUpper == "ADRESSE PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_RUE,        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
   else if (tokenUpper == "ADRESSE COMPLETE PATIENT")
    {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_RUE,       m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey)+ "\n";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_CODE_POST, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey)+ " ";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_VILLE,     m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
     resolvToken.replace("\n","<br />");
    }
   else if (tokenUpper == "ADRESSE PATIENT UNE LIGNE")
    {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_RUE,       m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey)+ " ";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_CODE_POST, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey)+ " ";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_VILLE,     m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
     resolvToken.replace("\n"," ");
    }

 } // Fin if == A
 else if (tokenUpper[0] == 'B')
 {
   if (tokenUpper == "BMI PATIENT")
    {   //....... Utilise la fonction calc pour renvoyer la bonne valeur
        QString un      = "1";
        QString mult    = "*";
        QString demande = "BMI";
        CDevilCalc *pCalc = new CDevilCalc(this);
        if (pCalc)
           {resolvToken = pCalc->Calc(un, mult, demande);
            delete pCalc;
           }
    }

 } // Fin if == B
 else if (tokenUpper[0] == 'C')
 {
   if ( tokenUpper == "CP MEDECIN")
    {resolvToken =  m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_CODE_POST, m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
   else if ( tokenUpper == "CONVENTION MEDECIN")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_CONVENTION, m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
   else if (tokenUpper == "CODE POSTAL PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_CODE_POST,  m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }


 } // Fin if == C
 else if (tokenUpper[0] == 'D')
 {
   if (tokenUpper == "DATE TIRET" || tokenUpper == "DD-MM-YYYY" ||
       tokenUpper == "DATE"|| tokenUpper == "CURRENT DATE"|| tokenUpper == "DATECOURANTE")
    {QDateTime qdt = QDateTime::currentDateTime ( Qt::LocalTime );
     QString format = "dd-MM-yyyy";
     resolvToken = qdt.toString ( format );
    }
   else if (token.left(3) == "DT=")
    {QDateTime qdt = QDateTime::currentDateTime ( Qt::LocalTime );
     resolvToken = qdt.toString (  token.mid(3) ).replace('|','h');
    }
   else if (tokenUpper == "DATE VISITE" ||tokenUpper == "DATE OBSERVATION")
    {CRubRecord  *pCRubRecord = GetIDCurrentDoc(TR("Observation"));
     if (pCRubRecord)
        {QString  dt = pCRubRecord->m_Date.left(10);
         resolvToken = dt.mid(8) + "-" + dt.mid(5,2) + "-" + dt.left(4);
        }
    }
   else if (tokenUpper.left(13) == "DATE DOCUMENT")
    {
     int pos_deb      = token.find('=');
     if (pos_deb==-1)
     {resolvToken = "Syntax error in DATE DOCUMENT '=' not found";
      return ;
     }
     int pos_end      = token.find("FORMAT");
     if (pos_end==-1)
     {resolvToken = "Syntax error in DATE DOCUMENT 'FORMAT' not found";
      return ;
     }
     ++pos_deb;    // passer le signe egal
     QString doc_typ  = token.mid(pos_deb, pos_end - pos_deb).stripWhiteSpace();
     pos_deb          = token.find('=', pos_end + 6);
     if (pos_deb==-1)
     {resolvToken = "Syntax error in DATE DOCUMENT '=' after 'FORMAT' not found";
      return ;
     }
     ++pos_deb;    // passer le signe egal
     QString format   = token.mid(pos_deb);

     if (doc_typ.length()==0)
     {resolvToken = "Syntax error in DATE DOCUMENT Document type missed after 'DATE DOCUMENT'"; return ;}
     if (format.length()==0)
     {resolvToken = "Syntax error in DATE DOCUMENT format date  missed after 'FORMAT'";         return ;}
     //int  id = GetIDCurrentDoc(typ_doc.toLong());
     CRubRecord  *pCRubRecord; 
     if (doc_typ.upper() == "THIS") pCRubRecord = m_pCRubCurrentRecord;
     else                           pCRubRecord = GetIDCurrentDoc(doc_typ); 
     if (pCRubRecord==0)
     {resolvToken = TR("Syntax error in DATE DOCUMENT Document type : '") + doc_typ + "' not found";
      return ;
     }
     QDate    qdt =  QDate::fromString ( pCRubRecord->m_Date, Qt::ISODate );
     resolvToken  =  qdt.toString (format);
    }
   else if (tokenUpper == "DATE NAISSANCE PATIENT")
    {m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_NSS, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey, &qvar);
     QDate qDteNss = qvar.toDate();
     resolvToken   = qDteNss.toString(QString("dd-MM-yyyy"));
    }
   else if (tokenUpper == "DATE DERNIERES REGLES")
    {   QString ret = GetDDR();
        if (ret != " ")
          {  QDate DDR;
             DDR = DDR.fromString(ret , Qt::ISODate);
             ret = DDR.toString("dddd dd MMM yyyy");
          }
        resolvToken = ret;
    }
   else if (tokenUpper.left(4) == "DDR=")
    {   QString ret = GetDDR();
        if (ret != " ")
          {  QDate DDR;
             DDR = DDR.fromString(ret , Qt::ISODate);
             ret = DDR.toString(token.mid(4) ).replace('|','h');
          }
        resolvToken = ret;
    }

 } // Fin if == D
}


//------------------------------ exeItemsFrom_E_To_H ---------------------------------------
/*! \brief Interprète les items de scripts débutant par la lettre E à H */
void CDevilCrucible::exeItemsFrom_E_To_H(QString &resolvToken, QString token, QString &document)
{ QString tokenUpper = token.upper();
  QVariant qvar;
 if (tokenUpper[0] == 'E')
 {
  if (tokenUpper == "EMAIL PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME,
                                        m_pMB->m_DOSS_IDENT_EMAIL, m_pMB->m_DOSS_IDENT_REF_PK,
                                        m_IdentPrimKey);
    }

 } // Fin == E
 else if (tokenUpper[0] == 'F')
 {

 } // Fin == F
 else if (tokenUpper[0] == 'G')
 {
   if (tokenUpper == "GET LAST IMAGE NAME")
    {if (G_pCApp->m_pDrTux)
        {MyEditText *pMyEditText  = G_pCApp->m_pDrTux->currentEditor();
         if (pMyEditText)
            {resolvToken = pMyEditText->GetLastImageName();
             int     pos = resolvToken.findRev('.');
             if ( pos   != -1) {resolvToken = resolvToken.left(pos) + ".png";}
            }
        }
    }
   else if (tokenUpper == "GET LAST IMAGE SRC_NAME")
    {resolvToken="";
     if (G_pCApp->m_pDrTux)
        {MyEditText *pMyEditText  = G_pCApp->m_pDrTux->currentEditor();
         if (pMyEditText) resolvToken = G_pCApp->m_C_ImageList.getSrcImageName(pMyEditText->GetLastImageName());
        }
    }

   else if (tokenUpper.left(8) == "GET SAVE")   // GET SAVE FILE NAME
    { int pos = token.find('=');
      if (pos!=-1)
         {QString   preselect = token.mid(pos+1).stripWhiteSpace();
          QString   filter    = TR("Tous fichiers (*)");
          pos         = preselect.find(',');
          if (   pos != -1)
             {filter    = preselect.mid(pos+1).stripWhiteSpace();
              preselect.truncate(pos);
             }
          if ( QDir::isRelativePath ( preselect ) ) preselect  = QDir::cleanDirPath (preselect.prepend(G_pCApp->m_PathAppli) );
          resolvToken = QFileDialog::getSaveFileName(preselect, filter, 0, TR("Enregistrer sous ..."), TR("Choisissez un nom de fichier pour enregistrer"));
         }
     else resolvToken="";
    }
 } // Fin == G
 else if (tokenUpper[0] == 'H')
 {
   if (tokenUpper == "HHHMM"||tokenUpper == "HEURECOURANTE")
    {QDateTime qdt = QDateTime::currentDateTime ( Qt::LocalTime );
     QString format = "hh:mm";
     resolvToken = qdt.toString ( format );
    }
 } // Fin == H

}

//------------------------------ exeItemsFrom_E_To_H ---------------------------------------
/*! \brief Interprète les items de scripts débutant par la lettre I à L */
void CDevilCrucible::exeItemsFrom_I_To_L(QString &resolvToken, QString token, QString &document)
{ QString tokenUpper = token.upper();
  QVariant qvar;
 if (tokenUpper[0] == 'I')
 {
   if (token.left(2) == "IF")  //  {{IF sexe = Féminin|S||}}
    {int op  =  0;
     int pos = -1;
     if      (op==0 && (pos =token.find('='))  != -1) {op = 1; }
     else if (op==0 && (pos =token.find('>'))  != -1) {op = 2; }
     else if (op==0 && (pos =token.find('<'))  != -1) {op = 3; }
     else if (op==0 && (pos =token.find(">=")) != -1) {op = 4; }
     else if (op==0 && (pos =token.find("<=")) != -1) {op = 5; }
     else if (op==0 && (pos =token.find("!=")) != -1) {op = 6; }
     else if (op==0 && (pos =token.find("%=")) != -1) {op = 7; }
     else if (op==0 && (pos =token.find("=%")) != -1) {op = 8; }
     else if (op==0 && (pos =token.find("%%")) != -1) {op = 9; }

     if (op == 0) resolvToken =  "Syntax error: operator not find";
     else
        {QString val          = token.mid(2, pos - 2).stripWhiteSpace();
         float  fVal          = val.toFloat();
         switch (op)
           {case 1: {QStringList values = QStringList::split('|', token.mid(pos+1), TRUE);
                     if (val==values[0].stripWhiteSpace() ) resolvToken = values[1].stripWhiteSpace();
                     else                                   resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 2: {QStringList values = QStringList::split('|', token.mid(pos+1), TRUE);
                     if (fVal>values[0].stripWhiteSpace().toFloat() )  resolvToken = values[1].stripWhiteSpace();
                     else                                   resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 3: {QStringList values = QStringList::split('|', token.mid(pos+1), TRUE);
                     if (fVal<values[0].stripWhiteSpace().toFloat() )  resolvToken = values[1].stripWhiteSpace();
                     else                                   resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 4: {QStringList values = QStringList::split('|', token.mid(pos+2), TRUE);
                     if (fVal>=values[0].stripWhiteSpace().toFloat() )  resolvToken = values[1].stripWhiteSpace();
                     else                                    resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 5: {QStringList values = QStringList::split('|', token.mid(pos+2), TRUE);
                     if (fVal <= values[0].stripWhiteSpace().toFloat() )  resolvToken = values[1].stripWhiteSpace();
                     else                                    resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 6: {QStringList values = QStringList::split('|', token.mid(pos+2), TRUE);
                     if (val != values[0].stripWhiteSpace() )  resolvToken = values[1].stripWhiteSpace();
                     else                                    resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 7: {QStringList values = QStringList::split('|', token.mid(pos+2), TRUE);
                     if (val.startsWith(values[0].stripWhiteSpace() ))  resolvToken = values[1].stripWhiteSpace();
                     else                                    resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 8: {QStringList values = QStringList::split('|', token.mid(pos+2), TRUE);
                     if (val.endsWith(values[0].stripWhiteSpace() ))  resolvToken = values[1].stripWhiteSpace();
                     else                                    resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
            case 9: {QStringList values = QStringList::split('|', token.mid(pos+2), TRUE);
                     if (val.contains(values[0].stripWhiteSpace() ))  resolvToken = values[1].stripWhiteSpace();
                     else                                    resolvToken = values[2].stripWhiteSpace();
                     break;
                    }
           } // endswitch (op)
        }
    }
   else if ( tokenUpper == "IDENTIFICATION MEDECIN")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_NUM_ORDRE, m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
   else if (tokenUpper.left(11)== "INTERVENANT") //{{INTERVENANT = Médecine generale CHAMPS DESIRES = NPA DECALAGE=}}
    {QStringList listData;
     int pos_deb         = token.find('=');
     if (pos_deb==-1)    {resolvToken = "Syntax error in INTERVENANT '=' not found";  return ;}
     int pos_end         = token.find("CHAMPS DESIRES");
     if (pos_end==-1)    {resolvToken = "Syntax error in INTERVENANT 'CHAMPS DESIRES' not found";  return ;}
     ++pos_deb;    // passer le signe egal
     QString qualite  = token.mid(pos_deb, pos_end - pos_deb).stripWhiteSpace();
     pos_deb          = token.find('=', pos_end + 14);
     if (pos_deb==-1)    {resolvToken = "Syntax error in INTERVENANT '=' after 'CHAMPS DESIRES' not found"; return ;}
     ++pos_deb;    // passer le signe egal
     pos_end          = token.find("DECALAGE", pos_deb);
     if (pos_end==-1)    {resolvToken = "Syntax error in INTERVENANT 'DECALAGE' not found";    return ;}
     QString validStr = token.mid(pos_deb, pos_end - pos_deb).stripWhiteSpace();
     pos_deb          = token.find('=', pos_end + 8);
     if (pos_deb==-1)    {resolvToken = "Syntax error in INTERVENANT '=' after 'DECALAGE' not found";  return ;}
     QString decalage = token.mid(pos_deb+1);
     m_pMB->PatientIntervenantsGetData(m_IdentPrimKey , listData, qualite, validStr, decalage);
     if (listData.count())
        {resolvToken = listData[0];  // on prend le premier medecin traitant de la liste
        }
    }
   else if (token.left(13)== "INTERV_STRUCT") //{{INTERV_STRUCT_XXX = Médecine generale STYLE = liste CHAMPS DESIRES = TNPSACV DECALAGE=    }}
    {
     QStringList listData;
     //......................... specification champ intervenant .....................
     int pos_deb         = token.find('=');
     if (pos_deb==-1)    {resolvToken = "Syntax error in INTERV_STRUCT first '=' not found";     return ;}
     QString prefix_ID   = token;
     prefix_ID.truncate(pos_deb);
     prefix_ID = prefix_ID.mid(13);
     prefix_ID = prefix_ID.stripWhiteSpace();

     int pos_end         = token.find("STYLE");
     if (pos_end==-1)    {resolvToken = "Syntax error in INTERV_STRUCT 'STYLE' not found";     return ;}
     ++pos_deb;    // passer le signe egal
     QString specialite  = token.mid(pos_deb, pos_end - pos_deb).stripWhiteSpace();

     pos_deb             = token.find('=', pos_end + 5);
     if (pos_deb==-1)    {resolvToken = "Syntax error in INTERV_STRUCT '=' after 'STYLE' not found";   return;}
     pos_end             = token.find("CHAMPS DESIRES");
     if (pos_end==-1)    {resolvToken = "Syntax error in INTERV_STRUCT 'CHAMPS DESIRES' not found";   return ;}
     ++pos_deb;    // passer le signe egal
     QString style       = token.mid(pos_deb, pos_end - pos_deb).stripWhiteSpace();

     pos_deb          = token.find('=', pos_end + 14);
     if (pos_deb==-1)    {resolvToken = "Syntax error in INTERV_STRUCT '=' after 'CHAMPS DESIRES' not found"; return ;}
     ++pos_deb;    // passer le signe egal
     pos_end          = token.find("DECALAGE", pos_deb);
     if (pos_end==-1)    {resolvToken = "Syntax error in INTERV_STRUCT 'DECALAGE' not found";   return ;}
     QString validStr = token.mid(pos_deb, pos_end - pos_deb).stripWhiteSpace();

     pos_deb          = token.find('=', pos_end + 8);
     if (pos_deb==-1)    {resolvToken = "Syntax error in INTERV_STRUCT '=' after 'DECALAGE' not found";    return ;}
     QString decalage = token.mid(pos_deb+1);
     DoFusionIntervenantStruct(m_IdentPrimKey , specialite, validStr, prefix_ID, style, decalage, document);
    }
 } // Fin == I
 else if (tokenUpper[0] == 'J')
 {

 } // Fin == J
 else if (tokenUpper[0] == 'K')
 {

 } // Fin == K
 else if (tokenUpper[0] == 'L')
 {

 } // Fin == L
}

//------------------------------ exeItemsFrom_M_To_P ---------------------------------------
/*! \brief Interprète les items de scripts débutant par la lettre M à P */
void CDevilCrucible::exeItemsFrom_M_To_P(QString &resolvToken, QString token, QString &document)
{ QString tokenUpper = token.upper();
  QVariant qvar;
 if (tokenUpper[0] == 'M')
 {

 } // Fin == M
 else if (tokenUpper[0] == 'N')
 {
  if (tokenUpper[1] == 'O')
   {if (tokenUpper == "NOM PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_INDEX_TBL_NAME, m_pMB->m_DOSS_INDEX_NOM,
                                        m_pMB->m_DOSS_INDEX_PRIM_KEY , m_IdentPrimKey);
    }
   else if (tokenUpper == "NOM PRENOM PATIENT")
    {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_DOSS_INDEX_TBL_NAME, m_pMB->m_DOSS_INDEX_NOM,
                                         m_pMB->m_DOSS_INDEX_PRIM_KEY , m_IdentPrimKey)+" ";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_DOSS_INDEX_TBL_NAME, m_pMB->m_DOSS_INDEX_PRENOM,
                                         m_pMB->m_DOSS_INDEX_PRIM_KEY , m_IdentPrimKey);
    }
   else if ( tokenUpper == "NOM PRENOM MEDECIN")
     {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_NOM,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk) + " ";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_PRENOM,
                                         m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
   else if ( tokenUpper == "NOM MEDECIN")
    {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_NOM,
                                         m_pMB->m_USER_IDENT_PRIMKEY ,    m_UserDocPk);
    }
   else if (tokenUpper == "NOM PRENOM ASSURE")
    {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_NOM_ASS,    m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey)+" ";
     resolvToken += m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_PRENOM_ASS, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
   else if (token.left(22)== "NOM PRENOM INTERVENANT")
    {QStringList listPraticiens;
     int pos         = token.findRev('=');
     QString qualite = token.mid(pos+1).stripWhiteSpace();
     m_pMB->PatientIntervenantsGetData(m_IdentPrimKey , listPraticiens, qualite,"NP", " ");   //  "NP"  pour nom prenom intervenant
     if (listPraticiens.count())
        {resolvToken = listPraticiens[0];  // on prend le premier medecin traitant de la liste
        }
    }
   else if (tokenUpper == "NOMBRE D'ENFANT PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_NB_ENF,
                                        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
   }  // fin == NO
  else if (tokenUpper[1] == 'U')
   {if (tokenUpper == "NUM. TEL3 PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_TEL3,
                                        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
   else if (tokenUpper == "NUM. TEL2 PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_TEL2,
                                        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
   else if (tokenUpper == "NUM. TEL1 PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_TEL1,
                                        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
   else if (tokenUpper == "NUM. SECU PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME,  m_pMB->m_DOSS_IDENT_NUMSS,
                                        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
   } // Fin == NU
 } // Fin == N
 else if (tokenUpper[0] == 'O')
 {
   if (tokenUpper.left(14) == "OPEN_FILE_NAME")
    {resolvToken="";
     if (G_pCApp->m_pDrTux)
        {int pos                = token.find('=');
         QString preselect      = "";
         if (pos!=-1) preselect = token.mid(pos+1);
         resolvToken = G_pCApp->GetImageFileName(0, preselect, 1);
        }
    } 

 } // Fin == O
 else if (tokenUpper[0] == 'P')
 {
  if (tokenUpper == "POIDS PATIENT")
    {   //....... Utilise la fonction calc pour renvoyer la bonne valeur
        QString un        = "1";
        QString mult      = "*";
        QString demande   = "POIDS";
        CDevilCalc *pCalc = new CDevilCalc(this);
        if (pCalc)
           {resolvToken = pCalc->Calc(un, mult, demande);
            delete pCalc;
           }
    }
   else if (tokenUpper == "PRENOM PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_INDEX_TBL_NAME, m_pMB->m_DOSS_INDEX_PRENOM,
                                        m_pMB->m_DOSS_INDEX_PRIM_KEY , m_IdentPrimKey);
    }
   else if ( tokenUpper == "PRENOM MEDECIN")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_PRENOM,
                                        m_pMB->m_USER_IDENT_PRIMKEY ,  m_UserDocPk);
    }
   else if (tokenUpper == "PROFESSION PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_PROF,
                                        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
 } // Fin == P
}



//------------------------------ exeItemsFrom_Q_To_T ---------------------------------------
/*! \brief Interprète les items de scripts débutant par la lettre Q à T */
void CDevilCrucible::exeItemsFrom_Q_To_T(QString &resolvToken, QString token, QString &document)
{ QString tokenUpper = token.upper();
  QVariant qvar;
 if (tokenUpper[0] == 'Q')
 {

 } // Fin == Q
 else if (tokenUpper[0] == 'R')
 {

 } // Fin == Q
 else if (tokenUpper[0] == 'S')
 {
   if ( tokenUpper == "SEXE MEDECIN")
    {resolvToken =  m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_SEXE,
                                         m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
   else if (tokenUpper == "SEXE PATIENT")
    {QString sexe = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_SEX,
                                         m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
     if (sexe=="F")     resolvToken=TR("Féminin");
     else               resolvToken=TR("Masculin");
    }
   else if ( tokenUpper == "SPECIALITE MEDECIN")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_QUALITE,
                                        m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
   else if (token.left(20) == "SAVE LAST IMAGE NAME")
    { int pos = token.find('=');
      if (pos!=-1)
         {QPixmap retPixMap;
          QString path = token.mid(pos+1).stripWhiteSpace();
          if ( QDir::isRelativePath ( path ) ) path  = QDir::cleanDirPath (path.prepend(G_pCApp->m_PathAppli) );
          if ( !path.endsWith("/") ) path += "/";
          MyEditText *pMyEditText  = G_pCApp->m_pDrTux->currentEditor();
          if ( pMyEditText && QFile::exists( path ) ) // si repertoire existe
             {path += pMyEditText->GetLastImageName();
              pos = path.findRev('.');
              if ( pos != -1) {path = path.left(pos) + ".png"; }
              if (G_pCApp->m_C_ImageList.getPixmap( pMyEditText->GetLastImageName() , retPixMap)) 
                 { QImageIO iio;
                   QImage   im;
                   im = retPixMap; // convert to image
                   iio.setImage( im );
                   iio.setFileName( path );
                   iio.setFormat( "PNG" );
                   iio.write();
                 }
             }
         }
     resolvToken="";
    }
   else if (tokenUpper.left(7) == "SAVE CU")
    { int pos = token.find('=');
      if (pos!=-1)
         {QPixmap retPixMap;
          QString path = token.mid(pos+1).stripWhiteSpace();
          if ( QDir::isRelativePath ( path ) ) path  = QDir::cleanDirPath (path.prepend(G_pCApp->m_PathAppli) );
          MyEditText *pMyEditText  = G_pCApp->m_pDrTux->currentEditor();
          if ( pMyEditText  ) // si repertoire existe
             {QString text = pMyEditText->text();
              CGestIni::Param_UpdateToDisk(path,text);
             }
         }
     resolvToken="";
    }
   else if (tokenUpper == "SURFACE PATIENT")
    {   //....... Utilise la fonction calc pour renvoyer la bonne valeur
        QString un      = "1";
        QString mult    = "*";
        QString demande = "SURFACE";
        CDevilCalc *pCalc = new CDevilCalc(this);
        if (pCalc)
           {resolvToken = pCalc->Calc(un, mult, demande);
            delete pCalc;
           }
    }
 } // Fin == S
 else if (tokenUpper[0] == 'T')
 {
   if (tokenUpper ==  "TITRE PS")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_TITRE, m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
   else if (tokenUpper == "TITRE PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_TITRE, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
     if (resolvToken=="")
        {m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_NSS, m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey, &qvar);
         QDate qDteNss = qvar.toDate();
         int age = atoi(m_pMB->CalculeDiffAge(&qDteNss, 0, FALSE, 2));
         if (age < 15)          resolvToken="L'enfant";
         if (age >= 15 && age <= 21)
            {QString sexe = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_SEX,
                                                 m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
             if (sexe=="F")     resolvToken="Mademoiselle";
             else               resolvToken="Mr";
            }
         if (age>21)
            {QString sexe = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_SEX,
                                                 m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
             if (sexe=="F")     resolvToken="Mme";
             else               resolvToken="Mr";
            }
        }
    }
   else if (tokenUpper == "TAILLE PATIENT")
    {   //....... Utilise la fonction calc pour renvoyer la bonne valeur
        QString un      = "1";
        QString mult    = "*";
        QString demande = "TAILLE";
        CDevilCalc *pCalc = new CDevilCalc(this);
        if (pCalc)
           {resolvToken = pCalc->Calc(un, mult, demande);
            delete pCalc;
           }
    }
   else if (tokenUpper == "TERME GROSSESSE")
    {   QString ret = "";
        ret = GetDDR();
        if (ret[0] == 'L') resolvToken = QString(" ");
        else    resolvToken = Terme_Obst ( ret , QDate::currentDate());
    }

 } // Fin == T
}

//------------------------------ exeItemsFrom_U_To_X ---------------------------------------
/*! \brief Interprète les items de scripts débutant par la lettre U à X */
void CDevilCrucible::exeItemsFrom_U_To_X(QString &resolvToken, QString token, QString &document)
{ QString tokenUpper = token.upper();
  QVariant qvar;
 if (tokenUpper[0] == 'U')
 {
  if (tokenUpper[5] == 'A')
   { if (tokenUpper == "USER ADRESSE COMPLETE MEDECIN" )
     {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_RUE,
                                          m_pMB->m_USER_IDENT_PRIMKEY ,       m_UserActuelPk)  + "\n";
      resolvToken += m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_CODE_POST,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk)  + " ";
      resolvToken += m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_VILLE,
                                          m_pMB->m_USER_IDENT_PRIMKEY ,     m_UserActuelPk) ;
      resolvToken.replace("\n","<br />");
     }
     else if (tokenUpper == "USER ADRESSE MEDECIN" )
     {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_RUE,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
      resolvToken.replace("\n","<br />");
     }
   } // Fin == USER A*
   else if (tokenUpper[5] == 'C')
   { if (tokenUpper == "USER CP MEDECIN" )
     {resolvToken =  m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_CODE_POST,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
     }
    else if (tokenUpper == "USER CONVENTION MEDECIN")
     {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_CONVENTION,
                                         m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
     }
   } // Fin == USER C*
   else if (tokenUpper[5] == 'N')
   { if (tokenUpper == "USER NOM PRENOM MEDECIN" )
     {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_NOM,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk) + " ";
      resolvToken += m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_PRENOM,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
     }
     else if (tokenUpper == "USER NOM MEDECIN" )
     {resolvToken  = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_NOM,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
     }
   } // Fin == USER N*
   else if (tokenUpper[5] == 'S')
   { if (tokenUpper == "USER SEXE MEDECIN" )
     {resolvToken =  m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_SEXE,
                                          m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
     }
     else if (tokenUpper == "USER SPECIALITE MEDECIN" )
     {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_QUALITE,
                                         m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
     }
   } // Fin == USER S*
   else if (tokenUpper[5] == 'T')
   { if (tokenUpper == "USER TITRE PS" )
     {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_TITRE,
                                        m_pMB->m_USER_IDENT_PRIMKEY ,   m_UserActuelPk);
     }
   } // Fin == USER T*
   else if (tokenUpper[5] == 'V')
   { if (tokenUpper == "USER VILLE MEDECIN" )
     {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_VILLE,
                                         m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
     }
   } // Fin == USER V*
   else if (tokenUpper == "USER IDENTIFICATION MEDECIN" )
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_NUM_ORDRE,
                                        m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
    }
   else if (tokenUpper == "USER PRENOM MEDECIN" )
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_PRENOM,
                                        m_pMB->m_USER_IDENT_PRIMKEY , m_UserActuelPk);
    }

 } // Fin == U
 else if (tokenUpper[0] == 'V')
 {
    if (tokenUpper == "VILLE PATIENT")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_DOSS_IDENT_TBL_NAME, m_pMB->m_DOSS_IDENT_VILLE,
                                        m_pMB->m_DOSS_IDENT_REF_PK , m_IdentPrimKey);
    }
    else if ( tokenUpper == "VILLE MEDECIN")
    {resolvToken = m_pMB->GetFieldValue(m_pMB->m_USER_IDENT_TBL_NAME, m_pMB->m_USER_IDENT_VILLE,
                                        m_pMB->m_USER_IDENT_PRIMKEY , m_UserDocPk);
    }
 } // Fin == V
 else if (tokenUpper[0] == 'W')
 {

 } // Fin == W
 else if (tokenUpper[0] == 'X')
 {

 } // Fin == X
}


//------------------------------ exeItemsFrom_Y_To_Z ---------------------------------------
/*! \brief Interprète les items de scripts débutant par la lettre Y à Z */
void CDevilCrucible::exeItemsFrom_Y_To_Z(QString &resolvToken, QString token, QString &document)
{ QString tokenUpper = token.upper();
  QVariant qvar;
 if (tokenUpper[0] == 'Y')
 {

 } // Fin == Y
 else if (tokenUpper[0] == 'Z')
 {


 } // Fin == Z

}

//-------------------------------------- DoFusionIntervenantStruct ----------------------------------------------------------------------------
void CDevilCrucible::DoFusionIntervenantStruct(const char *primKeyPat ,
                                               const QString &specialite,
                                               QString validStr,
                                               QString &prefix_ID,
                                               const QString &style,
                                               const QString &decalage,
                                               QString &text) //Médecin généraliste
{ QStringList pkIntervList;
  int i_style      = 0;  // champs nommes
  validStr.remove(' ');
  validStr.remove(',');
  prefix_ID.prepend("{{INTERV_CHAMP");
  prefix_ID += "_";                             // la prefix_ID doit etre de la forme :  {{INTERV_CHAMP_XXX_
  if (style.left(1).upper()=="L") i_style = 1;
  //................. Preparer la requete .....................................
  if (m_pMB->m_DataBase->isOpen()==FALSE && m_pMB->m_DataBase->open()==FALSE)  return ;

  //................. recuperer tous les PK des intervenants sur ce patient ..................................................
  QString requete("");
  requete  += "SELECT " +   m_pMB->m_DOSS_INTERVENANTS_INTERVPK  + " FROM " + m_pMB->m_DOSS_INTERVENANTS_TBL_NAME   + " WHERE ";
  requete  +=               m_pMB->m_DOSS_INTERVENANTS_PAT_PK    + " = '"   + primKeyPat                            + "' ";
  QSqlQuery query(requete , m_pMB->m_DataBase );

  //.................. si la requète a un resultat ..............................................
  if (query.isActive())
     { while (query.next())
         {pkIntervList.append( query.value( 0 ).toString());
         }
     }
  QString fieldToRetrieve = "SELECT ";
  char *pt = (char *) (const char*)validStr;
  while (*pt)
      {fieldToRetrieve += IdentValidCarToFieldName(pt[0]);
       ++pt;
       if (*pt) fieldToRetrieve += ",";
      }
  if (pkIntervList.count()==0)  {m_pMB->m_DataBase->close(); return;}
  for ( QStringList::Iterator rt = pkIntervList.begin(); rt != pkIntervList.end(); ++rt )
      {requete   = fieldToRetrieve;
       requete  += " FROM " + m_pMB->m_INTERVENANTS_TBL_NAME + " WHERE ";
       requete  +=   m_pMB->m_INTERVENANTS_PK    + " = '"    + *rt + "' ";
       if (specialite.length()) requete  += "AND "    + m_pMB->m_INTERVENANTS_SPEC + " = '" + specialite + "'";
       query.exec( requete );
       if (query.isActive())
          {QString ofset = decalage ;
           while (query.next())
              {int       i = 0;
               int       f = 0;
                        pt = (char *) (const char*)validStr;
               while (*pt)        // explorer les champs ainsi que determines par la liste de validation
                     {QString  result = m_pMB->Utf8_Query(  query, i++  );
                      if (i_style)           // champs listes de la forme INTERV_CHAMP_XXX_1 INTERV_CHAMP_XXX_2
                         {QStringList str_list = QStringList::split('\n', result.replace("\r\n", "\n"));
                          for ( QStringList::Iterator it = str_list.begin(); it != str_list.end(); ++it )
                              { text.replace(prefix_ID + QString::number(++f) + "}}", ofset + *it);
                              }
                         }
                      else                  // champs nommes de la forme INTERV_CHAMP_XXX_NOM INTERV_CHAMP_XXX_PRENOM
                         { switch (*pt)
                              {case 'N': text.replace(prefix_ID + "NOM}}",        ofset + result);    break;
                               case 'P': text.replace(prefix_ID + "PRENOM}}",     ofset + result);    break;
                               case 'A': {result               = result.replace("\r\n", "\n");
                                          QStringList adr_list = QStringList::split('\n',result);
                                          int n = 0;
                                          for ( QStringList::Iterator it = adr_list.begin(); it != adr_list.end(); ++it )
                                              { text.replace(prefix_ID + "ADR_" + QString::number(++n) + "}}", ofset + *it);
                                              }
                                         } break;
                               case 'C': text.replace(prefix_ID + "CODE_POST}}",  ofset + result);    break;
                               case 'V': text.replace(prefix_ID + "VILLE}}",      ofset + result);    break;
                               case 'T': text.replace(prefix_ID + "TITRE}}",      ofset + result);    break;
                               case 'S': text.replace(prefix_ID + "SPECIALITE}}", ofset + result);    break;
                              }
                         } //endif (i_style) champs listes ou cnamps nommes
                      ++pt;
                     } //end while (*pt)
              } // end while (query.next()) (ne devrait y en avoir qu'un)
          } // endif (query.isActive())
      }
  m_pMB->m_DataBase->close();
  return ;
}

//-------------------------------------- IdentValidCarToFieldName ----------------------------------------------------------------------------
QString CDevilCrucible::IdentValidCarToFieldName(int c)
{switch (c)
   {case 'N': return m_pMB->m_INTERVENANTS_NOM;
    case 'P': return m_pMB->m_INTERVENANTS_PRENOM;
    case 'A': return m_pMB->m_INTERVENANTS_RUE;
    case 'C': return m_pMB->m_INTERVENANTS_CODE_POST;
    case 'V': return m_pMB->m_INTERVENANTS_VILLE;
    case 'T': return m_pMB->m_INTERVENANTS_TITRE;
    case 'S': return m_pMB->m_INTERVENANTS_SPEC;
   }
 return QString("");
}

//--------------------------------- GetIDCurrentDoc -----------------------------------------------------
/*! \brief Retourne le CRubRecord correspondant au document actuellement affiché dans la CMDI gérant le doc_type.
*/
CRubRecord  *CDevilCrucible::GetIDCurrentDoc(const QString &doc_type)
{CRubRecord  *pCRubRecordRet = 0;
 //................... si non précisé retourner la rubrique courante..............................
 if (doc_type.length()==0||doc_type==TR("*"))
    { CMDI_Generic *pCMDI_Generic   =  G_pCApp->GetCurrentRubrique();
      if (pCMDI_Generic) 
         {RUBREC_LIST::iterator rit = pCMDI_Generic->Current_RubList_Iterator();
          if (rit != m_pRubList->end())
             { pCRubRecordRet = &(*rit);
               return pCRubRecordRet;
             }
         }
    }
 //................... on cherche celui de la map (ceux en cours d'affichage)..............................
 DOCUMENT_DISPLAY_MAP::Iterator it = m_pCurDisplayDocMap->find ( doc_type );
 if (! (it == m_pCurDisplayDocMap->end()) ) return it.data();
 //...................................... si pas trouve on va rechercher le dernier .........................
 //                                       correspondant au type numerique dans la liste generale
 QString doc_type_num = doc_type;
 if ( ! (doc_type[0]>='0' && doc_type[0]<='9') ) // si pas exprime sous forme numerique alors on convertit
    {doc_type_num = DocNameToDocNumType(doc_type);
    }
 //................... si pas trouve on cherche le dernier correspondant...................................
 CRubRecord  *pCRubRecord    = 0;
 RUBREC_LIST::Iterator ut ;

 for ( ut = m_pRubList->begin(); ut != m_pRubList->end(); ++ut )
     {pCRubRecord = &(*ut);
      if ( pCRubRecord->m_Type==doc_type_num) pCRubRecordRet = pCRubRecord;
     }
 return pCRubRecordRet;
}


//--------------------------- DocNameToDocNumType --------------------------------------------------
/*! \brief Transforme le nom de la rubrique en son équivalent numérique. Exemple : "Observation" -> "20030000"
*/
QString CDevilCrucible::DocNameToDocNumType(const QString &doc_name, int part /* = 8 */)
{QString ret = "";

 const char *c = doc_name;
 switch (*c)
   {case 'U':                             ret = "20040000"; break;
    case 'O':                             ret = "20030000"; break;
    case 'P':                             ret = "20020000"; break;
    case 'T':                             ret = "20060000"; break;
    case 'D':                             ret = "20080000"; break;
    case 'C':
         {if (doc_name== "Courrier")      ret = "20020500"; break;
          if (doc_name== "Certificat")    ret = "20020300"; break;
         }
   }
  return ret.left(part);
}


//---------------------------- ResolvScriptTokenSyntax ----------------------------------------
/*! \brief Interpréteur des fonctions de scripts ().
 * Les fonctions de scripts permettent de faire des actions au sein du document passé en paramètre.
 * L'appel de fonctions de scripts se présente de la sorte :
 * \code {{:: FONCTION_DE_SCRIPT ( Param1, param2 ....) }} \endcode
 * Appel la fonction exeFunction().
 * \sa exeFunction()
 */
int CDevilCrucible::ResolvScriptTokenSyntax(QString &resolvToken, QString token)
{resolvToken = "";
 //resolvToken = "test_token"; return 1;
 if (token[0] !='#')   CHtmlTools::HtmlToAscii(token);
 else                  token.remove (0, 1); 
 int pos_prem_p =  token.find('(');
 if (pos_prem_p ==-1)  {resolvToken = TR("Syntax Error first '(' not found in : ") + token; return 0;}
 int pos_last_p =  findLastDelimiter(token, ')');
 if (pos_last_p ==-1)  {resolvToken = TR("Syntax Error last ')' not found in : ")  + token; return 0;}
 resolvToken = exeFunction(token.left(pos_prem_p).stripWhiteSpace(), token.mid(pos_prem_p + 1, pos_last_p-pos_prem_p-1));
 return 1;
}

//----------------------------------- exeFunction -----------------------------------------------
/*! \brief interprète les fonction de script passées en paramètre : extract, extract_traitement, extract_table...
 * Les fonctions supportées actuellement :
 * - calc : fonction de calcul simple -> CDevilCalc
 * - copy_files :
 * - daysTo : Calcul le nombre de jour séparant deux dates.
 * - exe_process : Execute un process externe à DrTux.
 * - extract : extrait du texte du document -> Func_Extract()
 * - extract_traitement : extrait le traitement en cours -> Func_Extract_TTT()
 * - extract_table : extrait les terrains -> Func_Extract_Table()
 * - extract_var : extrait les variables de terrain -> Func_Extract_VAR()
 * - extract_atcd : Extrait les antécédents -> Func_Extract_ATCD()
 * - form_pdf : autocompletion de formulaires PDF -> CDevilFormPDF
 * - graph : graphiques biométriques -> CDevilGraph
 * - if : obsolete
 * - insert_image : insère une image
 * - intitule : modifie l'intitulé du document courant
 * - upper : met en majuscule
 * - lower : met en minuscule
 * - replace_image :
 * - sous_type : modifie le sous-type du document courant
 *
 * Le tableau de pointeurs FunctionDispatcher distribue les strings contenant la fonction et ses paramètres aux fonctions de scripts qui sont morcellées par ordre alphabetique.
 *
 */
QString CDevilCrucible::exeFunction(QString function_in, QString arg_str )
{//return QString("test_token");
 QChar pt = function_in[0];
 int i = pt.upper().unicode();
 i = i - 65; // A == 0
 i = i / 4;  // de 4 en 4 dans l'alphabet
 if ((i >= 0) && (i < 7)) // reste dans le tableau sinon erreur
 { return (this->*(FunctionDispatcher[i]))(function_in, arg_str);
 }
 // erreur si exécution de cette ligne
 return "";
}

//------------------------------ exeFunctionsFrom_A_To_D ---------------------------------------
/*! \brief Interprète les fonctions de scripts débutant par la lettre A à D */
QString CDevilCrucible::exeFunctionsFrom_A_To_D(QString &func, QString& args)
{ QString funcUpper = func.upper();
  QStringList arg_list;
  extractArgList(arg_list, args);

 if (funcUpper[0] == 'A')
 { 

 } // Fin if == A
 else if (funcUpper[0] == 'B')
 {
 
 } // Fin if == B
 else if (funcUpper[0] == 'C')
 {
   if (funcUpper == "CALC")
    {int nb = arg_list.count();
     QString ret = "";
     if (nb<4) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=3; ++i) arg_list.append("");
        }
     CDevilCalc *pCalc = new CDevilCalc(this);
     if (pCalc) 
        {ret =  pCalc->Calc(arg_list[0], arg_list[1], arg_list[2]);
         delete pCalc;
        }
     return ret;
    }
   else if (funcUpper.left(10) == "COPY_FILE")
    {QString ret = "";
     int      nb = arg_list.count();
     if (nb<1)          return TR("::copy_file() 1er  argument du nom du fichier source manquant");
     if (nb<2)          return TR("::copy_file() 2ème argument du nom de fichier ou répertoire de destination manquant");
     QString            action   = "";
     QString            imageSrc = arg_list[0].stripWhiteSpace();
     QString            imageDst = arg_list[1].stripWhiteSpace();
     QString            imgName  = QFileInfo(imageSrc).fileName();
     if (nb>=3)         imgName  = arg_list[2].stripWhiteSpace();
     if (nb>=4)         action   = arg_list[4].stripWhiteSpace();
     if ( !QFileInfo(imageDst).isDir ()) imgName = QFileInfo(imageDst).fileName();   // si le fichier de destination n'est pas un répertoire en extraire le nom du fichier de destination
     CHtmlTools::Copy_File(imageSrc, QFileInfo(imageDst).dirPath(), &imgName);
     if (action.find("remove_src")!= -1) QDir().remove(imageSrc);
     return ret;
    } 

 } // Fin if == C
 else if (funcUpper[0] == 'D')
 {
    if (funcUpper == "DAYSTO")
    {int nb = arg_list.count();
     if (nb<4) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=3; ++i) arg_list.append("");
        }
     return daysTo(arg_list[0], arg_list[1]);
    }
   else if (funcUpper == "DELETE_FILE")
    {QString    ret = "";
     QString action = "";
     int         nb = arg_list.count();
     if (nb<1)          return TR("::delete_file() 1er argument du nom du fichier source manquant");
     QString            path = arg_list[0].stripWhiteSpace();
     if (nb>1)        action = arg_list[1].stripWhiteSpace();
     if (QDir::isRelativePath ( path ) )  path = QDir::cleanDirPath ( path.prepend(G_pCApp->m_PathAppli) );
     if (action.length() && action.find("clear") != -1)
        {QFile qfile(path);
         if (qfile.open(IO_WriteOnly)==TRUE) 
            {qfile.close();
            }
        }
     else 
        {QDir().remove(RelPathToAbsPath(path));
        }
     return ret;
    } 
 } // Fin if == D

 // Rien n'est trouvé si on exécute la ligne suivante
 return "";

}


//------------------------------ exeFunctionsFrom_E_To_H ---------------------------------------
/*! \brief Interprète les fonctions de scripts débutant par la lettre E à H */
QString CDevilCrucible::exeFunctionsFrom_E_To_H(QString &func, QString& args)
{ QString funcUpper = func.upper();
  QStringList arg_list;
  extractArgList(arg_list, args);

 if (funcUpper[0] == 'E')
 {

   if (funcUpper == "EXE_PROCESS")
    {//............. récupère les arguments passés à la fonction ................
     QString ret   = "";
     QString arg   = "";
     //QString debug = "";
     bool   isComm = FALSE;
     int        nb = arg_list.count();
     if (nb>=2) //  completer la liste si < 5
        {int i;
         QProcess proc(0,"CDevilCrucibleProcess");
         QString pathExe = arg_list[1].stripWhiteSpace();

         QString mode    = arg_list[0].stripWhiteSpace().lower();            // waitEnd ou pass
         if ( pathExe.find("$IsAbsPath") != -1 )
           {  pathExe  = pathExe.remove("$IsAbsPath").stripWhiteSpace();
              isComm   = TRUE;
           }
         else if ( QDir::isRelativePath ( pathExe ) )
           {  pathExe  = QDir::cleanDirPath (pathExe.prepend(G_pCApp->m_PathAppli) );
           }
         if ( pathExe.find("$IsBinPath") != -1 )
            {pathExe   = pathExe.remove("$IsBinPath").stripWhiteSpace();
            }
         #ifdef Q_WS_MAC
         else
            {pathExe  +=  ".app/Contents/MacOS/" + QFileInfo(pathExe).baseName() ;                   // 0  Chemin de l'executable plugin à actionner
            }
         #else
            pathExe  +=  F_EXT ;                   // 0  Chemin de l'executable plugin à actionner
         #endif
         if ( isComm || QFile::exists(pathExe ) )
            {   proc.addArgument( pathExe );                   // 0  Chemin de l'executable plugin a actionner
                //debug  += pathExe + "\r\n";
                for( i=2; i<nb; ++i) 
                   { arg         = arg_list[i];
                     proc.addArgument( RelPathToAbsPath(arg) );
                   }
                if ( m_PluginRun == "" && proc.start() )
                   { m_PluginRun = pathExe;
                     G_pCApp->processEvents ();
                     while (mode == "waitend" && proc.isRunning ())
                        {
                          #ifdef Q_WS_WIN
                            Sleep (1);
                          #endif
                          #ifdef Q_WS_X11
                            sleep (1);
                          #endif
                          #ifdef Q_WS_MAC
                          #endif

                          QTimer timer;               // pour etre sur qu'un evenement se produise
                          timer.start(100, FALSE);    // sinon boucle sans fin dans    processEvents ( QEventLoop::WaitForMore )
                          QApplication::eventLoop()->processEvents ( QEventLoop::WaitForMore );
                        }
                     m_PluginRun = "";
                    }  // endif ( proc.start() )
                else
                    {QMessageBox::warning ( 0, TR ("ERREUR : EXE_PROCESS") ,
                                                  TR("Le processus : \r\n%1\r\n n'a pas démarré. ").arg(pathExe),
                                                  TR ("&Annuler"), 0, 0,
                                                  1, 1 );
                    }
           } // endif (QFile::exists( pathExe ) )
        else
           {QMessageBox::warning ( 0, TR ("ERREUR : EXE_PROCESS") ,
                                                  TR("Le processus : \r\n%1\r\n n'a pas été trouvé. ").arg(pathExe),
                                                  TR ("&Annuler"), 0, 0,
                                                  1, 1 );
           }
       }   //endif (nb>=2)
    return ret;
   }
   else if (funcUpper == "EXTRACT")
    {int nb = arg_list.count();
     if (nb<5) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=5; ++i) arg_list.append("");
        }
     return Func_Extract(arg_list[0], arg_list[1], arg_list[2], arg_list[3], arg_list[4]);
    }
   else if (funcUpper == "EXTRACT_TRAITEMENT")
    {int nb = arg_list.count();
     if (nb<6) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=6; ++i) arg_list.append("");
        }
     return Func_Extract_TTT(arg_list[0], arg_list[1], arg_list[2], arg_list[3], arg_list[4], arg_list[5]);
    }
   else if (funcUpper == "EXTRACT_TABLE")
    {int nb = arg_list.count();
     if (nb<10) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=10; ++i) arg_list.append("");
        }
     return Func_Extract_Table(arg_list[0], arg_list[1], arg_list[2], arg_list[3], arg_list[4],
                               arg_list[5], arg_list[6], arg_list[7], arg_list[8], arg_list[9]);
    }
   else if (funcUpper == "EXTRACT_VAR")
    {int nb = arg_list.count();
     if (nb<5) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=5; ++i) arg_list.append("");
        }
     return Func_Extract_VAR(arg_list[0], arg_list[1], arg_list[2], arg_list[3], arg_list[4]);
    }
   else if (funcUpper == "EXTRACT_ATCD")
    {int nb = arg_list.count();
     if (nb<6) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=6; ++i) arg_list.append("");
        }
     return Func_Extract_ATCD(arg_list[0], arg_list[1], arg_list[2], arg_list[3], arg_list[4], arg_list[5]);
    }


 } // Fin == E
 else if (funcUpper[0] == 'F')
 {
   if (funcUpper == "FORM_PDF")
    {//............. récupère les arguments passés à la fonction ................
     int      nb = arg_list.count();
     QString ret = "";
     if (nb<3) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=2; ++i) arg_list.append("");
        }
     CDevilFormPDF *pPDF = new CDevilFormPDF (this);
     if (ret!=0)
        {ret = pPDF->Form_PDF(arg_list[0]);
         delete pPDF;
        }
     return ret;
    }

 } // Fin == F
 else if (funcUpper[0] == 'G')
 {
   if (funcUpper == "GRAPH")
    {//............. récupère les arguments passés à la fonction ................
     int nb = arg_list.count();
     if (nb<3) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=2; ++i) arg_list.append("");
        }
     CDevilGraph *pGraph = new CDevilGraph ( this );
     if (pGraph) 
        {pGraph->Graph(arg_list[0]);
         delete pGraph;
        }
     return QString(" ");
    }

 } // Fin == G
 else if (funcUpper[0] == 'H')
 {

 } // Fin == H

 // Rien n'est trouvé si on exécute la ligne suivante
 return "";
}

//------------------------------ RelPathToAbsPath ---------------------------------------
QString & CDevilCrucible::RelPathToAbsPath(QString &path)
{ int pos     = -1;
  int deb     = -1;
  QString tmp = "";
  if ((pos = path.findRev("$EndToAbsPath")) != -1)
     {do
       { if ( (deb = path.findRev("$ToAbsPath"),pos) != -1)
           { path = path.remove(pos,13);   // virer le $EndToAbsPath
             tmp  = QDir::cleanDirPath (G_pCApp->m_PathAppli + path.mid(deb+10, pos - deb -10).stripWhiteSpace());
             path = path.replace (deb, pos - deb, tmp );
             pos  = path.findRev("$EndToAbsPath", deb);
           }
        else
           { pos = -1;
           }
      } while (pos != -1);
       return path;
     }
   else if ( (pos = path.find("$ToAbsPath")) != -1) 
     { path = path.left(pos) + QDir::cleanDirPath (G_pCApp->m_PathAppli + path.mid(pos+10).stripWhiteSpace());
       return path;
     }
 return path;
}

//------------------------------ exeFunctionsFrom_I_To_L ---------------------------------------
/*! \brief Interprète les fonctions de scripts débutant par la lettre I à L */
QString CDevilCrucible::exeFunctionsFrom_I_To_L(QString &func, QString& args)
{ QString funcUpper = func.upper();
  QStringList arg_list;
  extractArgList(arg_list, args);

 if (funcUpper[0] == 'I')
 {
   if (funcUpper == "INTITULE")
    {if (m_pCRubCurrentRecord)
        {// Cette fonction ajoute ou remplace l'intitulé de l'observation en cours
         // UTILISATION =
         //  {{:: Intitule ( Intitulé du document , add  ) }}
         //  {{:: Intitule ( Intitulé du document , change  ) }}
         //  {{:: Intitule ( Intitulé du document ) (équivalent de "change") }}
         //............. récupère les arguments passés à la fonction ................
         int nb = arg_list.count();
         if (nb<6) //  completer la liste si < 5
            {int i;
             for( i=nb; i<=2; ++i) arg_list.append("");
            }

         arg_list[0] = arg_list[0].stripWhiteSpace();
         arg_list[1] = arg_list[1].stripWhiteSpace();
         // ......... modifie l'intutilé de l'observation
         if      (arg_list[1].lower() == "add")     {m_pCRubCurrentRecord->m_Libelle.append(" " + arg_list[0]);}
         else if (arg_list[1].lower() == "change")  {m_pCRubCurrentRecord->m_Libelle = arg_list[0]; }
         else if (arg_list[1].lower() == "")        {m_pCRubCurrentRecord->m_Libelle = arg_list[0]; }
         //.......... recharger le combo ..................................................................
         QString qstr = m_pCRubCurrentRecord->m_Date.left(10) + " " + m_pCRubCurrentRecord->m_Libelle;
         CMDI_Generic *pCMDI_Generic =  G_pCApp->GetCurrentRubrique();
         if (pCMDI_Generic) pCMDI_Generic->ChangeCurrentComboBoxItemName(qstr);
        }
     return QString("");
    }
   else if (funcUpper == "IMPORT")
    {int                       nb = arg_list.count();  if (nb<1) return TR("::import() 1er argument du fichier à importer manquant");
     QString                 ret  = "";
     MyEditText     *pMyEditText  = 0; //G_pCApp->m_pDrTux->currentEditor();
     CMDI_Generic  *pCMDI_Generic = 0;
     QString        rubriqueCible = "";
     if (nb>2)      rubriqueCible = arg_list[2].stripWhiteSpace();

     if (rubriqueCible.length()==0) 
        {pCMDI_Generic = G_pCApp->GetCurrentRubrique();
         if (pCMDI_Generic)    pMyEditText = pCMDI_Generic->GetCurrentEditor();
         if (pMyEditText==0) rubriqueCible = CMDI_Documents::S_GetRubName();         // si pas d'éditeur dans la rubrique courante ou pas de rubrique courante
         else                rubriqueCible = pCMDI_Generic->m_RubName;
        }
     if (pMyEditText==0) 
        {pCMDI_Generic = (CMDI_Generic*) G_pCApp->m_pDrTux->IsExistRubrique(rubriqueCible);
         if (pCMDI_Generic==0) G_pCApp->m_pDrTux->OnActiverOrCreateRubrique(rubriqueCible);
         pCMDI_Generic = (CMDI_Generic*) G_pCApp->m_pDrTux->IsExistRubrique(rubriqueCible);
         if (pCMDI_Generic==0) return TR("::import() la rubrique de destination: \"") + rubriqueCible + TR("\" n'a pu être créé");
         pMyEditText = pCMDI_Generic->GetCurrentEditor();
        }

     if (pMyEditText) 
        {QString            destPath  = "";
         QString            imageFile = arg_list[0].stripWhiteSpace();
         QString         typeAction   = "new";
         if (nb>1)         typeAction = arg_list[1].stripWhiteSpace();
         if (nb>3)           destPath = arg_list[3].stripWhiteSpace();
         bool                 verbose = (typeAction.find("verbose")!=-1);
         if ( QDir::isRelativePath ( imageFile ) ) imageFile  = QDir::cleanDirPath ( imageFile.prepend(G_pCApp->m_PathAppli) );
         if ( QFileInfo(imageFile).isDir ())                                                 return ret;
         if ( ! QFile::exists( imageFile ) && typeAction.find("remove_src")== -1)            return verbose ? TR("::import() Fichier image : \"") + imageFile + TR("\" non trouvé") : "";
         QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
         if (QFileInfo(imageFile).size() <= 0)      {QApplication::restoreOverrideCursor();  return verbose ? TR("::import() Fichier image : \"") + imageFile + TR("\" vide") : "";  }              // si fichier vide on sort
         if (typeAction.length()==0) typeAction = "new";

         //.................. insertion html ............................................................
         QString toImport;
         CGestIni::Param_UpdateFromDisk(imageFile, toImport);
         if (typeAction.find("crlf2html")!=-1) {toImport = toImport.replace("\r\n", "<br>"); toImport = toImport.replace("\n", "<br>");toImport = toImport.replace("\r", "<br>");}
         if (destPath.length())
            {if ( QDir::isRelativePath ( destPath ) ) destPath  = QDir::cleanDirPath ( destPath.prepend(G_pCApp->m_PathAppli) );
             if ( !QFileInfo(destPath).exists() )
                {if (!QDir().mkdir ( destPath ) )
                    {QApplication::restoreOverrideCursor();
                     return verbose ? TR("::import() le répertoire de destination: \"") + destPath + TR("\" n'a pu être créé") : "";
                    }
                }
             CHtmlTools::Copy_File(imageFile, destPath);
             if (typeAction.find("clear_src") != -1)
                {QFile qfile(imageFile);
                 if (qfile.open(IO_WriteOnly)==TRUE) 
                    {qfile.close();
                    }
                }
            }
         if (typeAction.find("remove_src")!= -1)
            {QDir().remove(imageFile);
            }
         if ( typeAction.find("insert") != -1)                 // si reference est la première creer l'icone de l'image
            {if (typeAction.find("html") != -1)  CHtmlTools::insertHtml(pMyEditText, toImport,1);        // html remove
             else                                CHtmlTools::insertHtml(pMyEditText, toImport,0);        // keep html
            }
         else  if ( typeAction.find("replace") != -1)
            {pMyEditText->setText(toImport);
            }
         else  if ( typeAction.find("new") != -1)
            {pCMDI_Generic->AddNewDocument(toImport,  pCMDI_Generic->GetType() );
            }

         QApplication::restoreOverrideCursor();
        }
     return ret;
    }

   else if (funcUpper == "INSERT_IMAGE")
    {QString ret = "";
     int      nb = arg_list.count();
     if (nb<1)                                        return TR("::insert_image() 1er argument du fichier image manquant");
     MyEditText *pMyEditText  = G_pCApp->m_pDrTux->currentEditor();
     if (pMyEditText) 
        {QString            imageFile = arg_list[0].stripWhiteSpace();
         int                    max_w = 500;
         int                    max_h = -1;
         QString       renameAction   = 0;
         QString       destPath       = 0;
         if (nb>=2)             max_w = arg_list[1].toInt();
         if (nb>=3)             max_h = arg_list[2].toInt();
         if (nb>=4)      renameAction = arg_list[3].stripWhiteSpace();
         if (nb>=5)          destPath = arg_list[4].stripWhiteSpace();

         if ( QDir::isRelativePath ( imageFile ) ) imageFile  = QDir::cleanDirPath ( imageFile.prepend(G_pCApp->m_PathAppli) );
         if ( QFileInfo(imageFile).isDir ())                                                 return ret;
         if ( ! QFile::exists( imageFile ) && renameAction.find("remove_src")== -1)          return TR("::insert_image() Fichier image : \"") + imageFile + TR("\" non trouvé");
         QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
         if (QFileInfo(imageFile).size() <= 10)      {QApplication::restoreOverrideCursor(); return ret; }                // si fichier vide on sort
         QPixmap      img(imageFile);     // format image deviné car 'const char * format = 0'
         if ( img.isNull())                          {QApplication::restoreOverrideCursor(); return TR("::insert_image() l'image : \"") + imageFile + TR("\" n'a pu être créée");}
         //............... mise à l'echelle de l'image .....................................
         int w             = img.width ();
         int h             = img.height();
         float coefReduc_w = 1.00;
         float coefReduc_h = 1.00;
         if (max_h != -1 && w > max_w) coefReduc_w = (float)max_w / (float) w;
         if (max_h != -1 && h > max_h) coefReduc_h = (float)max_h / (float) h;
         if (coefReduc_w < 1.00 || coefReduc_h < 1.00)
            {if (coefReduc_h < coefReduc_w) coefReduc_w = coefReduc_h;         // on va se servir de coefReduc_w = au coeff de réduction le plus petit 
             w = (int) ((float) w * coefReduc_w);
             h = (int) ((float) h * coefReduc_w);
             QImage   qi    = img.convertToImage().smoothScale (w, h );
             img            = QPixmap (qi );
            }
         //.................. insertion html ............................................................
         QString    imgName    = CMoteurBase::GUID_Create()+"-h"+ QString::number(h)+"w"+QString::number(w) + ".png";                  // nom et clef pour retrouver l'image
         if (renameAction.length())
            {if ( QDir::isRelativePath ( destPath ) ) destPath  = QDir::cleanDirPath ( destPath.prepend(G_pCApp->m_PathAppli) );
             if ( !QFileInfo(destPath).exists() )
                {if (!QDir().mkdir ( destPath ) )
                    {QApplication::restoreOverrideCursor();
                     return TR("::insert_image() le répertoire de destination: \"") + destPath + TR("\" n'a pu être créé");
                    }
                }
             CHtmlTools::Copy_File(imageFile, destPath, &imgName);
             if (renameAction.find("clear_src") != -1)
                {QFile qfile(imageFile);
                 if (qfile.open(IO_WriteOnly)==TRUE) 
                    {qfile.close();
                    }
                }
             if (renameAction.find("remove_src")!= -1)
                {QDir().remove(imageFile);
                }
             imageFile = destPath + "/" + imgName;
            }
         if ( G_pCApp->m_C_ImageList.appendToList( &img , imgName, imageFile) == 1 )                 // si reference est la première creer l'icone de l'image
            {ret  = "<img src=\"";
             ret += imgName;
             ret += "\" width=\""  + QString::number(w);
             ret += "\" height=\"" + QString::number(h);
             ret += "\">";
            }
         QApplication::restoreOverrideCursor();
        }
     return ret;
    }


 } // Fin == I
 else if (funcUpper[0] == 'J')
 {

 } // Fin == J
 else if (funcUpper[0] == 'K')
 {

 } // Fin == K
 else if (funcUpper[0] == 'L')
 {

   if (funcUpper == "LOWER")
    {int nb = arg_list.count();
     if (nb<4) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=3; ++i) arg_list.append("");
        }
     return arg_list[0].lower();
    }

 } // Fin == L

 // Rien n'est trouvé si on exécute la ligne suivante
 return "";

}

//------------------------------ exeFunctionsFrom_M_To_P ---------------------------------------
/*! \brief Interprète les fonctions de scripts débutant par la lettre M à P */
QString CDevilCrucible::exeFunctionsFrom_M_To_P(QString &func, QString& args)
{ QString funcUpper = func.upper();
  QStringList arg_list;
  extractArgList(arg_list, args);

 if (funcUpper[0] == 'M')
 {

 } // Fin == M
 else if (funcUpper[0] == 'N')
 {if (funcUpper == "NOUVEAU DOCUMENT")
    {int nb = arg_list.count();
     if (nb<4) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=3; ++i) arg_list.append("");
        }
     MyEditText     *pMyEditText  = 0; //G_pCApp->m_pDrTux->currentEditor();
     CMDI_Generic  *pCMDI_Generic = 0;
     QString        rubriqueCible = "";
     if (nb>=1)      rubriqueCible = arg_list[0].stripWhiteSpace();

     if (rubriqueCible.length()==0) 
        {pCMDI_Generic = G_pCApp->GetCurrentRubrique();
         if (pCMDI_Generic)    pMyEditText = pCMDI_Generic->GetCurrentEditor();
         if (pMyEditText==0) rubriqueCible = CMDI_Documents::S_GetRubName();         // si pas d'éditeur dans la rubrique courante ou pas de rubrique courante
         else                rubriqueCible = pCMDI_Generic->m_RubName;
        }
     if (pMyEditText==0) 
        {//pCMDI_Generic = (CMDI_Generic*) G_pCApp->m_pDrTux->IsExistRubrique(rubriqueCible);
         if (pCMDI_Generic==0) G_pCApp->m_pDrTux->OnActiverOrCreateRubrique(rubriqueCible);
         pCMDI_Generic = (CMDI_Generic*) G_pCApp->m_pDrTux->IsExistRubrique(rubriqueCible);
         if (pCMDI_Generic==0) return TR("::nouveau document() la rubrique de destination: \"") + rubriqueCible + TR("\" n'a pu être créé");
         pMyEditText = pCMDI_Generic->GetCurrentEditor();
        }

     if (pCMDI_Generic!=0) pCMDI_Generic->AddNewDocument(" ",  pCMDI_Generic->GetType() );
     return "";
    }
 } // Fin == N
 else if (funcUpper[0] == 'O')
 {

 } // Fin == O
 else if (funcUpper[0] == 'P')
 {

 } // Fin == P

 // Rien n'est trouvé si on exécute la ligne suivante
 return "";

}



//------------------------------ exeFunctionsFrom_Q_To_T ---------------------------------------
/*! \brief Interprète les fonctions de scripts débutant par la lettre Q à T */
QString CDevilCrucible::exeFunctionsFrom_Q_To_T(QString &func, QString& args)
{ QString funcUpper = func.upper();
  QStringList arg_list;
  extractArgList(arg_list, args);

 if (funcUpper[0] == 'Q')
 {

 } // Fin == Q
 else if (funcUpper[0] == 'R')
 {

   if (funcUpper == "REPLACE_IMAGE")
    {QString ret = "";
     int      nb = arg_list.count();
     if (nb<1)                                        return TR("::replace_image() argument du nom de l'image à remplacer manquant");        //{{GET LAST IMAGE NAME}}
     if (nb<2)                                        return TR("::replace_image() argument du fichier image de remplacement manquant");     //$ToAbsPath ../../tmp/{{GET LAST IMAGE NAME}}
     MyEditText *pMyEditText  = G_pCApp->m_pDrTux->currentEditor();
     if (pMyEditText) 
        {QString            imageName = arg_list[0].stripWhiteSpace();         // nom de l'image à replacer
         QString            imageFile = arg_list[1].stripWhiteSpace();         // nom de du fichier image de remplacement
         int                    max_w = 550;
         int                    max_h = -1;
         QString             action   = 0;
         QString             srcName  = 0;
         if (nb>=2)             max_w = arg_list[2].toInt();
         if (nb>=3)             max_h = arg_list[3].toInt();
         if (nb>=4)            action = arg_list[4].stripWhiteSpace();
         QPixmap                  img;
         //................ recuperer dimenssions originales ...........................
         G_pCApp->m_C_ImageList.getPixmap(imageName, img);
         int ori_w                    = img.width ();
         int ori_h                    = img.height();

         if ( QDir::isRelativePath ( imageFile ) ) imageFile  = QDir::cleanDirPath ( imageFile.prepend(G_pCApp->m_PathAppli) );
         if ( QFileInfo(imageFile).isDir ())                                                 return ret;
         if ( ! QFile::exists( imageFile ) )                                                 return TR("::replace_image() Fichier image : \"") + imageFile + TR("\" non trouvé");
         QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
         if (QFileInfo(imageFile).size() <= 10)      {QApplication::restoreOverrideCursor(); return TR("::replace_image() Fichier image : \"") + imageFile + TR("\" vide"); }                // si fichier vide on sort
         img = QPixmap(imageFile);     // format image deviné car 'const char * format = 0'
         if ( img.isNull())                         {QApplication::restoreOverrideCursor();  return TR("::replace_image() l'image : \"")       + imageFile + TR("\" n'a pu être créée");}
         //............... mise à l'echelle de l'image .....................................
         if (action.find("keep_Original_W")!= -1 && ori_w>0) max_w = ori_w;
         if (action.find("keep_Original_H")!= -1 && ori_h>0) max_h = ori_h;
         int w             = img.width ();
         int h             = img.height();
         float coefReduc_w = 1.00;
         float coefReduc_h = 1.00;
         if (max_w != -1 && w > max_w) coefReduc_w = (float)max_w / (float) w;
         if (max_h != -1 && h > max_h) coefReduc_h = (float)max_h / (float) h;
         if (coefReduc_w < 1.00 || coefReduc_h < 1.00)
            {if (coefReduc_h < coefReduc_w) coefReduc_w = coefReduc_h;         // on va se servir de coefReduc_w = au coeff de réduction le plus petit 
             w = (int) ((float) w * coefReduc_w);
             h = (int) ((float) h * coefReduc_w);
             QImage   qi    = img.convertToImage().smoothScale (w, h );
             img            = QPixmap (qi );
            }
         //...................... remplacer l'image d'origine de la liste des images ..............................................
         if (action.find("keep_src")!= -1)          srcName   = G_pCApp->m_C_ImageList.getSrcImageName(imageName);
         else                                       srcName   = imageFile;
         G_pCApp->m_C_ImageList.destroyFromList(imageName);                             // détruire dans la liste  tout ce qui concerne ce nom
         if ( G_pCApp->m_C_ImageList.appendToList( &img , imageName, srcName) == 1 )    // rajouter une l'image
            {QString t = pMyEditText->text (  );     // methode bourin pour forcer le redraw
             CHtmlTools::ReplaceImageTag(t, imageName, imageName,  w ,  h );
             pMyEditText->setText ( t );
             if (action.find("remove_src")!= -1)
                {QDir().remove(imageFile);
                }
            }
         QApplication::restoreOverrideCursor();
        }
     return ret;
    }

 } // Fin == Q
 else if (funcUpper[0] == 'S')
 {

   if (funcUpper == "SOUS_TYPE")
    {if (m_pCRubCurrentRecord)
        {// Cette fonction ajoute ou remplace l'intitulé de l'observation en cours
         // UTILISATION =
         //  {{:: sous_type ( sous type du document , add  ) }}
         //  {{:: sous_type ( sous type du document , change  ) }}
         //  {{:: sous_type ( sous type du document ) (équivalent de "change") }}
         //............. récupère les arguments passés à la fonction ................
         int nb = arg_list.count();
         if (nb<6) //  completer la liste si < 5
            {int i;
             for( i=nb; i<=2; ++i) arg_list.append("");
            }

         arg_list[0] = arg_list[0].stripWhiteSpace();
         arg_list[1] = arg_list[1].stripWhiteSpace();
         // ......... modifie l'intutilé de l'observation
         if      (arg_list[1].lower() == "add")     {m_pCRubCurrentRecord->m_SubType.append(" " + arg_list[0]);}
         else if (arg_list[1].lower() == "change")  {m_pCRubCurrentRecord->m_SubType = arg_list[0]; }
         else if (arg_list[1].lower() == "")        {m_pCRubCurrentRecord->m_SubType = arg_list[0]; }
         //.......... recharge le combo
        }
     return QString("");
    }

 } // Fin == S
 else if (funcUpper[0] == 'T')
 {


 } // Fin == T

 // Rien n'est trouvé si on exécute la ligne suivante
 return "";

}

//------------------------------ exeFunctionsFrom_U_To_X ---------------------------------------
/*! \brief Interprète les fonctions de scripts débutant par la lettre U à X */
QString CDevilCrucible::exeFunctionsFrom_U_To_X(QString &func, QString& args)
{ QString funcUpper = func.upper();
  QStringList arg_list;
  extractArgList(arg_list, args);

 if (funcUpper[0] == 'U')
 {

   if (funcUpper == "UPPER")
    {int nb = arg_list.count();
     if (nb<4) //  completer la liste si < 5
        {int i;
         for( i=nb; i<=3; ++i) arg_list.append("");
        }
     return arg_list[0].upper();
    }

 } // Fin == U
 else if (funcUpper[0] == 'V')
 {

 } // Fin == V
 else if (funcUpper[0] == 'W')
 {

 } // Fin == W
 else if (funcUpper[0] == 'X')
 {

 } // Fin == X

 // Rien n'est trouvé si on exécute la ligne suivante
 return "";

}


//------------------------------ exeFunctionsFrom_Y_To_Z ---------------------------------------
/*! \brief Interprète les fonctions de scripts débutant par la lettre Y à Z */
QString CDevilCrucible::exeFunctionsFrom_Y_To_Z(QString &func, QString& args)
{ QString funcUpper = func.upper();
  QStringList arg_list;
  extractArgList(arg_list, args);

 if (funcUpper[0] == 'Y')
 {

 } // Fin == Y
 else if (funcUpper[0] == 'Z')
 {


 } // Fin == Z

 // Rien n'est trouvé si on exécute la ligne suivante
 return "";

}

//------------------------------------------ daysTo -------------------------------------------------
/*! \brief Retourne le nombre de jours entre la date1 et la date2 */
QString CDevilCrucible::daysTo(const QString &date1, const QString &date2)
{QDate d2 = QDate::currentDate();
 if (date2.length()!=0) d2 = QDate::fromString (NormaliseDate(date2), Qt::ISODate );
 QDate d1 = QDate::fromString (NormaliseDate(date1), Qt::ISODate );
 return QString::number( d1.daysTo ( d2 ) );
}

//------------------------------------------ NormaliseDate -----------------------------
/*! \brief Retourne une date de la forme 12x09xx2007 sous forme 2007-09-12 */
QString CDevilCrucible::NormaliseDate(const QString &date)
{int end = date.length();
 int   i = -1;
 QString str;
 while (++i < end) { if (  (date[i]>='0' && date[i]<='9') ) str += date[i]; }
 if (str.length() ==6 )  str.insert ( 4, "20" ); // date forme 011207 --> 01122007
 if (str.length() !=8 )  return TR("format invalide");

 return str.mid(4,4)+"-"+str.mid(2,2)+"-"+str.mid(0,2);
}

//------------------------ GetDateOfActiveRubrique -------------------------------------
/*! \brief Retourne la date du document en cours */
QString CDevilCrucible::GetDateOfActiveRubrique()
{ if (m_pCRubCurrentRecord ) return m_pCRubCurrentRecord->m_Date;
  else                       return QString(" ");
}


//-------------------- GetPoidsTaille --------------------------------------------
/*! \brief Retourne le poids et la taille.
 *  \todo pour faciliter débuggage utiliser Var_Code
*/
void CDevilCrucible::GetPoidsTaille(const char* pt_doc, QString &tableau, QString &poids_name, QString &taille_name, QString &poids_var, QString &taille_var)
{poids_var      = "";
 taille_var     = "";
 if (pt_doc==0 || strlen(pt_doc)==0)    return;
 tableau        = tableau.stripWhiteSpace();
 poids_name     = poids_name.stripWhiteSpace();
 taille_name    = taille_name.stripWhiteSpace();
 //..................... recuperer la date ........................................
 char *pt = (char*) pt_doc;
 //........... effacer les anciennes tables de variables ................
 QString       strTab[10];
 QString      section   = "";
 QString     var_name   = "";

 while((pt = m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
    {if (section == tableau )                  // à ce stade pt pointe au debut de la nouvelle ligne
        {int               n_var   = 0;
         int    pos_poids_to_get   = -1;
         int   pos_taille_to_get   = -1;
         while (*pt && *pt != '[')
               {var_name = "";
                pt   = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &strTab[0], &strTab[1], &strTab[2], &strTab[3], &strTab[4], &strTab[5], &strTab[6], &strTab[7], &strTab[8]);
                //...................... DEFINITION DE VARIABLE .....................................
                //                       contruire le dialogue des variables
                if (var_name== TR("Modèle") )
                   {if (pos_poids_to_get  == -1 && strTab[1] == poids_name)  pos_poids_to_get  = n_var;   // noter la position de la variable
                    if (pos_taille_to_get == -1 && strTab[1] == taille_name) pos_taille_to_get = n_var;   // noter la position de la variable
                    n_var++;
                   } // endif (var_name=="Modèle")
                 //................ VALEURS DATEES DE VARIABLE ........................................
                 else if (pos_poids_to_get!= -1 && var_name !="" ) // var_name  reçoit la date
                   {poids_var = strTab[pos_poids_to_get];
                   }
                 //................ VALEURS DATEES DE VARIABLE ........................................
                 else if (pos_taille_to_get!= -1 && var_name !="" ) // var_name  reçoit la date
                   {taille_var = strTab[pos_taille_to_get];
                   }
               } // end while (*pt && *pt != '[')
         section="";
        } // endif (section.left(3)=="VAR" )
    } // end while((pt=m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
}

//----------------------- Func_Extract_TTT ------------------------------------------
/*! \brief Retourne le traitement en cours du patient.
*/
QString CDevilCrucible::Func_Extract_TTT(const QString &title, QString &linePatern, QString renouvInterc, QString &tableau, QString &poids_name, QString &taille_name)
{QString           resolvToken = "";
 QString           poids_var   = "";
 QString           taille_var  = "";

 QPL_CPosologie    ordoList;
 QStringList       lineList;
 //................ recuperer données du terrain .......................................
 QString  str_data = m_pMB->GetDataFromRubList( GetIDCurrentDoc(TR("Terrain")) );       // recuperer données soit dans liste cache soit sur disque
 if (str_data.length()==0)              return resolvToken;
 char* pt          = (char*) (const char*) str_data;
 renouvInterc      = renouvInterc.stripWhiteSpace();

 //................ recuperer le poids et taille .................................................
 GetPoidsTaille(pt, tableau, poids_name, taille_name, poids_var, taille_var);
 //........... effacer les anciennes tables de variables ................
 //
 QString val1, val2, val3, val4, val5, val6, val7, val8, val9, val10;
 QString      section   = "";
 QString     var_name   = "";

 char     *prop_section = 0;       // permet de noter le pointeur sur la section propriete renouvelable/intercurent des produits

 while((pt = m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
  {if (section == TR("Propriété Ordonnance"))                  // à ce stade pt pointe au debut de la nouvelle ligne
        {prop_section = pt;                                // noter section des proprietes (à traiter apres liste med)
         section      = "";
        }
     //.................................. TTT ........................................................
     // IMPORTANT: cette section doit tjrs etre en dernier dans le fichier pour des raisons de fin !!!
     if (section==TR("Traitement"))                  // à ce stade pt pointe au debut de la nouvelle ligne
      {  QString stringStruct;
         CMedicaBase::Medica_DiskDataSplitIn_HtmlData_StructData(pt, 0, &stringStruct); // y isoler et recuperer les données calculables
         CMedicaBase::Medica_PosologieListDeserialize(stringStruct, ordoList);

         if (linePatern.contains("tableau", FALSE) != 0)
          {     QString tabTTT = "<table width=\"100%\" border=\"1\" cellpadding=\"2\" cellspacing=\"2\">";
                tabTTT += "<tr><td width=\"50%\">"+TR("Médicament")+"</td><td width = \"10%\">"+TR("Matin")+"</td>";
                tabTTT += "<td width = \"10%\">"+TR("Midi")+"</td><td width = \"10%\">"+TR("Soir")+"</td>";
                tabTTT += "<td width = \"10%\">"+TR("Coucher")+"</td></tr>";
                int nb = 0;
                QString libMed = "";
                for (QPL_CPosologie::iterator it = ordoList.begin(); it != ordoList.end(); ++it )
                  {  ++ nb; (*it).m_NumOrdre = nb;
                     libMed = m_pMD->Medica_GetMedicamentNameByCIP((*it).m_MEDICA_POSO_CIP);
                     libMed  = libMed.section (" ", 0, 1); // fait de la place dans le libellé du médicament...
                     if ((*it).m_ALD == 1) // en ALD
                        { libMed.append("<span style=\"font-weight:600\">*</span>"); }
                     tabTTT += "<tr><td width=\"50%\">"+libMed+"</td>";
                     QString tmpPoso = (*it).m_MEDICA_POSO_REPART_PRISES;
                     if (tmpPoso.contains("MA", FALSE) != 0)
                        { tabTTT += "<td width = \"10%\">&nbsp;X&nbsp;</td>";}
                     else
                        { tabTTT += "<td width = \"10%\">&nbsp;</td>";}
                     if (tmpPoso.contains("MI", FALSE) != 0)
                        { tabTTT += "<td width = \"10%\">&nbsp;X&nbsp;</td>";}
                     else
                        { tabTTT += "<td width = \"10%\">&nbsp;</td>";}
                     if (tmpPoso.contains("SO", FALSE) != 0)
                        { tabTTT += "<td width = \"10%\">&nbsp;X&nbsp;</td>";}
                     else
                        { tabTTT += "<td width = \"10%\">&nbsp;</td>";}
                     if (tmpPoso.contains("CO", FALSE) != 0)
                        { tabTTT += "<td width = \"10%\">&nbsp;X&nbsp;</td>";}
                     else
                        { tabTTT += "<td width = \"10%\">&nbsp;</td>";}
                  } // Fin for
             tabTTT += "</table><br />";
             return tabTTT;
          } // fin if tableau

         int nb = 0;
         //int medALD = 0;
         QString tmp;
         for (QPL_CPosologie::iterator it = ordoList.begin(); it != ordoList.end(); ++it )
             {++ nb; (*it).m_NumOrdre = nb;

              QString lineToInsert = linePatern;

              lineToInsert.replace("::NUM::", QString::number(nb));
              lineToInsert.replace("::LIBELLE::", m_pMD->Medica_GetMedicamentNameByCIP((*it).m_MEDICA_POSO_CIP));
              lineToInsert.replace("::NB_PRISE::", (*it).m_MEDICA_POSO_NB_PRISES);
              lineToInsert.replace("::REPART_PRISES::", (*it).m_MEDICA_POSO_REPART_PRISES);
              if ((*it).m_ALD == 1)      { tmp = TR("En ALD");   }
              else if ((*it).m_ALD == 0) { tmp = TR("Hors ALD"); }
              lineToInsert.replace("::ALD::", tmp);
              lineToInsert.replace("::POSO_TEXTUELLE::", (*it).m_PosoTextuelle);
              lineToInsert.replace("::NOTE::", (*it).m_Note);
              lineToInsert.replace("::DUREE::", (*it).m_MEDICA_POSO_PENDANT);

              // m_pCMedicaBase->Medica_PosologieToSimpleText(*it, m_pCMDI_Terrain->GetPoids(), m_pCMDI_Terrain->GetTaille() );
              lineList.append(lineToInsert);
             } //end for

      break;
     } // end if section
  } // end while((pt=m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
 for ( QStringList::Iterator lt = lineList.begin(); lt != lineList.end(); ++lt )
     {resolvToken += *lt;
     }
 if (resolvToken.length()) resolvToken.prepend(title);

 return    resolvToken;
}

//---------------------- Func_Extract_Table ----------------------------------
/*! \brief Crée un tableau avec les variables du terrain en cours d'affichage.
 *  \todo Pour améliorer le débuggage utiliser Var_Code
 * {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE ,   0  , TOUTES )}}
 * {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , -24  , PREMIERE )}}
 * {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , +24  , DERNIERE )}}
 * {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , +-24 , PLUS PROCHE )}}
 * {{:: Extract_Table (VAR Constantes de Base , Pouls L=50  & SAO2 L=80  & Temp L=100 ,DATE VISITE ,   0  , TOUTES , H=50 ,E=3)}}
*/
QString CDevilCrucible::Func_Extract_Table(QString &section_VAR,
                                           QString &list_VAR,
                                           QString &date_deb,
                                           QString &date_end,
                                           QString &cell_W ,
                                           QString &cell_H,
                                           QString &border_W,
                                           QString &width,
                                           QString &dateTimeFormat,
                                           QString &dateTimeWidth)
{QString   resolvToken    = "";
 //.................. recuperer les données du terrain courant ..........................
 QString  str_data = m_pMB->GetDataFromRubList( GetIDCurrentDoc(TR("Terrain")) );       // recuperer données soit dans liste cache soit sur disque
 if (str_data.length()==0)              return resolvToken;
 char* pt          = (char*) (const char*) str_data;

 //..................... menage dans les espaces............................................
 section_VAR    = section_VAR.stripWhiteSpace();
 list_VAR       = list_VAR.stripWhiteSpace();
 date_deb       = date_deb.stripWhiteSpace();
 date_end       = date_end.stripWhiteSpace();
 cell_W         = cell_W.stripWhiteSpace();
 cell_H         = cell_H.stripWhiteSpace();
 border_W       = border_W.stripWhiteSpace();
 width          = width.stripWhiteSpace();
 dateTimeFormat = dateTimeFormat.stripWhiteSpace();
 dateTimeWidth  = dateTimeWidth.stripWhiteSpace();
 //..................... recuperer les dates et determiner le filtrage ............................................
 int dateMustBeFiltred = 0;
 if ( ! (date_deb == "TOUTE DATE" && date_end == "TOUTE DATE"))
    {CRubRecord *pCRubRecord  = 0;
     //............. date de fin ...........................
     if (date_end == "DATE VISITE")
        {pCRubRecord = GetIDCurrentDoc(TR("Observation"));
         if (pCRubRecord)
            {date_end          = pCRubRecord->m_Date;
             date_end          = date_end.left(10) + "T23:59:59";           // DATE VISITE finit à 23h59 de la date
             dateMustBeFiltred = 1;
            }
        }
     else if (date_end == "TOUTE DATE")
        { date_end          = "3000-01-01T00:00:00";
          //date_end = QDateTime::currentDateTime().addYears(100).toString(Qt::ISODate);  //"dd-MM-yyyyThh:mm:ss"
          dateMustBeFiltred = 1;
        }
     else if ( date_end == "DATE ACTUELLE")
        {date_end = QDateTime::currentDateTime().toString(Qt::ISODate);  //"dd-MM-yyyyThh:mm:ss"
         dateMustBeFiltred = 1;
        }
     //............. date de debut ...........................
     if (date_deb== "DATE VISITE")
        {if (pCRubRecord==0) pCRubRecord = GetIDCurrentDoc(TR("Observation"));
         if (pCRubRecord)
            {date_deb          = pCRubRecord->m_Date;
             date_deb          = date_deb.left(10) + "T00:00:00";      // DATE VISITE commence à 00h00 de la date
             dateMustBeFiltred = 1;
            }
         }
     else if ( date_deb == "TOUTE DATE" )
        { date_deb          = "1900-01-01T00:00:00";
          //date_deb = QDateTime::currentDateTime().addYears(-100).toString(Qt::ISODate);  //"dd-MM-yyyyThh:mm:ss"
          dateMustBeFiltred = 1;
        }
     else if ( date_deb == "DATE ACTUELLE")
        {date_deb = QDateTime::currentDateTime().toString(Qt::ISODate);  //"dd-MM-yyyyThh:mm:ss"
         dateMustBeFiltred = 1;
        }
    }

 //........... noter les dimensions du tableau ................
 //
 QString val1, val2, val3, val4, val5, val6, val7, val8, val9, val10;
 QString       strTab[10];
 QString      section       = "";
 QString     var_name       = "";
 QStringList varNameList    = QStringList::split(';', list_VAR);
 QString            w_max   = cell_W;
 QStringList        var_w;
 //.................. epurer la liste du nom de variable des '= 80' et relever largeurs .......................
 for ( QStringList::Iterator it = varNameList.begin(); it != varNameList.end(); ++it )
     { QString v = *it;
       int a_pos = v.find('=');

       if (a_pos != -1)                         // si largeur indiquée s'en servir
          { var_w.append(v.mid(a_pos+1));
            *it = v.left(a_pos).stripWhiteSpace();
          }
       else                                     // si largeur non indiquée prendre celle par feaut
          { var_w.append(w_max);
            *it = v.stripWhiteSpace();
          }
     }

 QValueList<int> pos_tab;
 QString    head_table = "";
 QString    body_table = "";
 int     isOkToDisplay = 0;


 //......................... dates de debut et de fin ...................................
 QDateTime qdt_Deb;
 QDateTime qdt_End;

 if ( dateMustBeFiltred )
    {qdt_Deb = QDateTime::fromString ( date_deb, Qt::ISODate );
     if    (date_end == "0" || date_end.length() <= 0)  qdt_End = qdt_Deb;
     else                                               qdt_End = QDateTime::fromString ( date_end, Qt::ISODate );
     int nb_h = 0;
     if      (date_end == "0" || date_end.length() <= 0)   //YYYY-MM-DDTHH:MM:SS
             {qdt_Deb = QDateTime::fromString ( date_deb.left(10)+"T00:00:00", Qt::ISODate );
              qdt_End = qdt_Deb.addSecs (   24 * 3600 );
              dateMustBeFiltred = 1;
             }
     else if (date_end.left(2) == "+-" || date_end.left(2) == "-+")
             {nb_h = date_end.mid(2).toInt();
              qdt_Deb.addSecs ( - nb_h * 3600 );
              qdt_End.addSecs (   nb_h * 3600 );
              dateMustBeFiltred = 1;
             }
     else if (date_end.left(1) == "+" )
             {nb_h = date_end.mid(1).toInt();
              qdt_End.addSecs (   nb_h * 3600 );
              dateMustBeFiltred = 1;
             }
     else if (date_end.left(1) == "-" )
             {nb_h = date_end.mid(1).toInt();
              qdt_Deb.addSecs ( - nb_h * 3600 );
              dateMustBeFiltred = 1;
             }
    }

 head_table           += "<TABLE cellSpacing=\"" + QString("1")       + "\"" +
                         " cellpadding=\""       + border_W           + "\"" +
                         " width=\""             + width              + "\"" +
                         " border=\""            + border_W           + "\"><TBODY> <TR>";

 while((pt = m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
    {if ( section == section_VAR )                  // à ce stade pt pointe au debut de la nouvelle ligne
        {// QString name_section = section.mid(4);
         // [VAR Poids]     1      2       3       4    5   6        7                                      8
         //    Modèle  = Date, Valeur, 31536000, 100, 50, Kg(s), Attention à la surcharge pondérale, Attention à l'amaigrissement
         //    2004-03-15T17:14:48 =  68
         //                                   val1                             ,    val2               val3      val4 val5 val6  val7       val8
         int            i      =  0;
         int        n_var      =  0;

         if (dateTimeFormat.length())
            {//.......................... titre de la colonne Date ..................................
             QString dateTimeTitle = "Date";
             int p  = dateTimeFormat.find('=');
             if (p !=-1) {dateTimeTitle  = dateTimeFormat.mid(p+1);
                          dateTimeFormat = dateTimeFormat.left(p);
                         }
             head_table += "<TD width=" + dateTimeWidth + ">" + dateTimeTitle + "</TD>";   // titre colonne  date
            }
         while (*pt && *pt != '[')
               {var_name = "";
                pt   = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &strTab[0], &strTab[1], &strTab[2], &strTab[3], &strTab[4], &strTab[5], &strTab[6], &strTab[7], &strTab[8]);
                //...................... definition des colonnes .....................................
                //
                if (var_name == TR("Modèle"))
                   {int ind  = varNameList.findIndex (  strTab[1] );                          // voir si ce nom de colonne est à afficher
                    if (ind != -1)
                       {pos_tab.append( n_var );                                              // noter la position de la colonne
                        head_table += "<TD width=" + var_w[ind] + QString(">") + strTab[1] + "</TD>";
                       }
                    else
                       { pos_tab.append( -1 );                                                // cette colonne n'est pas désirée à l'affichage
                       }
                    ++n_var;
                   } // endif (var_name=="Modèle")
                 //................ VALEURS DATEES DE VARIABLE ........................................
                 //                                             2004-03-15T17:14:48 =  68 , truc, 1000
                 else if ( n_var )                // NB :  var_name  reçoit la date
                   {int p_w      =  0;
                    int isDateOK =  1;

                    //................. filtrer selon les dates .......................................
                    QDateTime qdt = QDateTime::fromString ( var_name, Qt::ISODate );
                    if (dateMustBeFiltred)
                       {if (qdt>=qdt_Deb && qdt<=qdt_End) isDateOK = 1;
                        else                              isDateOK = 0;
                       }
                    if (isDateOK)
                       {body_table  += "<TR>\r\n";
                        if (dateTimeFormat.length() && qdt.isValid())  // afficher eventuellement la date
                            {var_name    = qdt.toString(dateTimeFormat);
                             body_table += "        <TD width=" + dateTimeWidth + ">" + var_name  + "</TD>\r\n";
                            }

                         for ( i = 0; i < n_var; ++i)     // parcourir toute les colonnes de variables
                             {if (pos_tab[i] != -1)       // cette colonne est elle à afficher si oui le faire
                                 {body_table += "        <TD width=" + var_w[p_w] + QString(">") + strTab[i] + "</TD>\r\n";
                                  ++p_w;
                                  ++isOkToDisplay;
                                 }
                             }
                         body_table += "</TR>\r\n";
                       } // end if (isDateOK)
                   } // end else if ( n_var )
               } // end while (*pt && *pt != '[')
         section="";
        } // endif (section.left(3)=="VAR" )
    }
 if (isOkToDisplay)
    {resolvToken = head_table + "</TR>\r\n" + body_table + "</TBODY></TABLE>\r\n";
    }
 return    resolvToken;
}



//---------------------- Func_Extract_ATCD ------------------------------------------
/*! \brief Fonction script de MédinTux qui permet d'accéder aux antécédents enregistrés des patients.
 * Plusieurs utilisations possibles de cette fonction : l'appel par "pattern" qui peut contenir
 * - ::LIBELLE::	Pour obtenir le libellé de l'antécédent
 * - ::MEDCHIR::	Pour le type d'antécédent (Médical, chirurgical, Facteur de risque, cardio-vasculaire...)
 * - ::ETAT::		Pour obtenir son état (guéri ou actif)
 * - ::CODE::		Pour obtenir le code CIM10 de l'antécédent
 * - ::NOTE::		Pour obtenir le commentaire de l'antécédent
 * - ::DATE::		Pour obtenir la date de l'antécédent.

 * L'appel du mode "linéaire" ou lisible permet d'obtenir une phrase digérée des antécédents. Pratique pour les courriers de correspondance.

 * Exemples d'utilisation dans les masques :
 *  \code   {{:: Extract_ATCD (lineaire) }}  \endcode
 *  \code   {{:: Extract_ATCD (Médicaux, Chirurgicaux, Allergies, Divers, ::LIBELLE:: (::NOTE::) en ::DATE::), en }}  \endcode
 *  \todo A CONNECTER AVEC G_pCApp->m_pAtcd_Code
*/
QString CDevilCrucible::Func_Extract_ATCD(const QString &medPrefix,
                                          const QString &chirPrefix,
                                          const QString &allergPrefix,
                                          const QString &diversPrefix,
                                          const QString &linePatern,
                                          const QString &datePrefix)
{QString   resolvToken = "";
 QString   str_data    = m_pMB->GetDataFromRubList( GetIDCurrentDoc(TR("Terrain")) );       // recuperer données soit dans liste cache soit sur disque
 if (str_data.length()==0)              return resolvToken;
 char *pt = (char*) (const char*)str_data;

 //........... effacer les anciennes tables de variables ................
 //
 QString val1, val2, val3, val4, val5;
 QString      section          = "";
 QString     var_name          = "";
 QString lineMedToInsert       = "";
 QString lineChirToInsert      = "";
 QString lineAllergToInsert    = "";
 QString lineDiversToInsert    = "";
 QString lineToInsert          = "";
 QStringList typesATCD;		// Reçoit la liste des types d'ATCD Médicaux reconnus par Medintux
 QStringList typesATCDChir;	// Reçoit la liste des types d'ATCD Chirurgicaux reconnus par Medintux
 QStringList linesATCD;  	// Liste mirroir qui contiendra les libellés d'ATCD Médicaux et leur date.
 QStringList linesATCDChir;  	// Liste mirroir qui contiendra les libellés d'ATCD Chirurgicaux et leur date.

 typesATCD.clear();
 typesATCDChir.clear();
 linesATCD.clear();
 typesATCD     = G_pCApp->m_pAtcd_Code->m_Rubriques_ATCD_Med;
 typesATCDChir = G_pCApp->m_pAtcd_Code->m_Rubriques_ATCD_Chir;
 uint i;
 for ( i = 0; i < typesATCD.count();++i)
 { linesATCD << " "; }
 for ( i = 0; i < typesATCDChir.count();++i)
 { linesATCDChir << " "; }
 bool trouve = FALSE;		// Au moment de la recherche du type d'ATCD TRUE = trouvé, FALSE cas contraire


 while((pt = m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
 { if (medPrefix.find(TR("lineaire")) >= 0)
   { //...................... Extract_ATCD (lineaire) ........................

//      if (!G_pCApp->m_pAtcd_Code) return "";
//      G_pCApp->m_pAtcd_Code->getAtcdToTxt(resolvToken);

     if (section==TR("Antécédents"))
     { while (*pt && *pt != '[')
       {var_name = "";
	pt = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &val1, &val2, &val3, &val4, &val5);
	// Nettoie et prépare la lisibilité des items de l'antécédent
	var_name.replace("\\,",",");	var_name.replace("\\;",";");	var_name.replace("\\[","[");
	var_name.replace("\\]","]");	val4.replace("\\,",",");	val4.replace("\\;",";");
	val4.replace("\\[","[");	val4.replace("\\]","]");

	// ATCD codé CIM 10 ==> alléger le libellé
	if (val3.left(1) == "~" )
	{ // Enlève SAI, le texte après la virgule, le texte entre parenthèse
	  var_name = var_name.replace(" SAI", "");
	  int deb  = var_name.find(",");
	  int fin  = -1;
	  if (deb != -1) var_name = var_name.left(deb);
	  int i = 0;
	   while ((var_name.find("(") != -1) || (i != 10))
	   { ++i;
	     deb = var_name.find("(");
	     fin = var_name.find(")", deb);
	     if (fin != -1)
	     { var_name = var_name.left(deb-1) + var_name.right(var_name.length() - fin - 1);
	     }
	     else break;
	   }
	  var_name = var_name.stripWhiteSpace();
	} // Fin ATCD CIM 10

	// Construction des lignes
	lineToInsert = var_name;                                // Libellé
	if (val4 != "") lineToInsert += " (" + val4 + ")";      // Note
	if (val5 != "") lineToInsert += " en " + val5.right(4); // Date
	trouve = FALSE;						// Initialisation avant recherche

	// Affecter à la bonne ligne selon catégories (médical, chirurgical, allergie)
	if (val3.left(1)=="(")         lineAllergToInsert += lineToInsert + ", ";
	else
	{  // Gestion compatibilité avec ancienne version de gestion des ATCD (Méd ou Chir)
	   if (val1==TR("Méd")) { linesATCD[0] += lineToInsert + ", "; trouve = TRUE; }
	   if (val1==TR("Chir")) { linesATCDChir[0] += lineToInsert + ", "; trouve = TRUE; }
	   else if (val1=="") { lineDiversToInsert += lineToInsert + ", "; trouve = TRUE; }
	   { // Recherche parmis les ATCD Médicaux
	     QString isChir = val1.left(4);
	     if (isChir != "Chir")
 	     { for (uint i = 0; i < typesATCD.count();++i)
	       { if (val1 == typesATCD[i]) 
	         { linesATCD[i] += lineToInsert + ", ";
	  	   trouve = TRUE;
	           break;
	         }
	       } // Fin For
	     } // Fin if isChir
	     // Si pas trouvé ==> recherche parmis les ATCD chirurgicaux
	     if (!trouve)
	     { for (uint i = 0; i < typesATCDChir.count();++i)
	       { if (val1 == typesATCDChir[i]) 
	         { linesATCDChir[i] += lineToInsert + ", ";
		   trouve = TRUE;
	           break;
	         }
	       } // Fin For
	     }
	     // Si toujours pas trouvé ==> met en Divers
	     if (!trouve) lineDiversToInsert += lineToInsert + ", ";
	   }

	}

	// Manque les ATCD gynéco-obstétriques 

       }  //  Fin while
       section="";
     }  // Fin if section == "Antécédents"

     // Construction des phrases à insérer
     lineToInsert = "";
     int i;
     for ( i = 0; i < (int)typesATCD.count();++i)
     { if (linesATCD[i] != " ") 
	{ lineToInsert += "- " + typesATCD[i] + "&nbsp;: " + linesATCD[i].left(linesATCD[i].length()-2) +"&nbsp;; " + "<br />\n";
	}
     }
     if (lineDiversToInsert != "")
	{ lineToInsert += "- " + TR("Autres") + "&nbsp;: " + lineDiversToInsert.left(lineDiversToInsert.length()-2) +"&nbsp;; " + "<br />\n";
	}
     if (lineAllergToInsert != "")
	{ lineToInsert += "- " + TR("Allergies") + "&nbsp;: " + lineAllergToInsert.left(lineAllergToInsert.length()-2) +"&nbsp;; " + "<br />\n";
	}
     for ( i = 0; i < (int)typesATCDChir.count();++i)
     { if (linesATCDChir[i] != " ") 
	{ lineToInsert += "- " + typesATCDChir[i] + "&nbsp;: " + linesATCDChir[i].left(linesATCDChir[i].length()-2) +"&nbsp;; " + "<br />\n";
	}
     }


//     lineToInsert = lineMedToInsert + lineChirToInsert + lineAllergToInsert + lineDiversToInsert;
     if (lineToInsert.length()<=0)
     { //  Le patient n'a pas d'ATCD ou ils ne sont pas renseignés....
       lineToInsert = TR("Sans antécédents connus.");
     }
     else
     {	// Prépare la ligne globale
	lineToInsert.prepend( TR("Ses antécédents sont marqués par : ") + "<br />\n" );

       // Mettre le point final à la phrase
       lineToInsert = lineToInsert.left(lineToInsert.length()-15) + ". <br />\n";
     }
     resolvToken = lineToInsert ;

   } // Fin if "lineaire"
   else
     {     //.................................. ATCD ........................................................;
           if (section==TR("Antécédents"))
             {
                while (*pt && *pt != '[')                      // libelle  med/chir    etat     code   commentaire date
                  {pt = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &val1, &val2, &val3, &val4, &val5);
                   var_name.replace("\\,",",");  var_name.replace("\\;",";");   var_name.replace("\\[","["); var_name.replace("\\]","]");
                   val4.replace("\\,",",");      val4.replace("\\;",";");       val4.replace("\\[","[");     val4.replace("\\]","]");

                   //...................... remplacer les paterne ...................................
                   lineToInsert = linePatern;
                   if (var_name.lower().endsWith(" sai")) var_name.truncate(var_name.length()-4);
                   lineToInsert.replace("::LIBELLE::", var_name);
                   lineToInsert.replace("::MEDCHIR::", val1);
                   lineToInsert.replace("::ETAT::",    val2);
                   lineToInsert.replace("::CODE::",    val3);
                   lineToInsert.replace("::NOTE::",    val4);
                   if (val5.length())
                      {val5.prepend(datePrefix);
                       lineToInsert.replace("::DATE::",    val5);
                      }
                   else
                      {lineToInsert.replace("::DATE::",    "");
                      }
                   //...................... affecter selon catégories ................................
                   if (val3.left(1)=="(")                       lineAllergToInsert += lineToInsert + "<br />";
                   else if (val1.left(3).lower()== TR("Méd"))   lineMedToInsert    += lineToInsert + "<br />";
                   else if (val1.left(3).lower()== TR("Chir"))  lineChirToInsert   += lineToInsert + "<br />";
                   else                                         lineDiversToInsert += lineToInsert + "<br />";
                  }  // Fin while *pt
                section="";
             }  // Fin if section = antécédents
        } // Fin ......... Extract_ATCD (1,2,3,4,5) ...........
    } // Fin while section

 if (medPrefix.find(TR("lineaire")) == -1)
    {
        if (lineToInsert.length()<=0)    return QString("");
        if (lineMedToInsert.length())    lineMedToInsert.prepend(medPrefix+"<br />");
        if (lineChirToInsert.length())   lineChirToInsert.prepend(chirPrefix+"<br />");
        if (lineAllergToInsert.length()) lineAllergToInsert.prepend(allergPrefix+"<br />");
        if (lineDiversToInsert.length()) lineDiversToInsert.prepend(diversPrefix+"<br />");
        resolvToken = lineMedToInsert + lineChirToInsert + lineAllergToInsert + lineDiversToInsert;
    }
 return    resolvToken;
}

//------------------------------------------ GetVarNeerer --------------------------------------------------------
/*! \brief Récupère toutes la valeurs de la variable la plus poche de la date passée en entrée
 * \param QString section         la section de la variable  (VAR Poids)
 * \param QString variable        la variable en question    (Poids)
 * \param QString dateSel         la date voulue en format String ISO
 * \return QString de la valeurs sans sa date
 * EXEMPLE :
 *      QString sect = "VAR Poids";
 *      QString varn = "Poids";
 *      QString datePlusProche = "2007-09-23T13:00:00"
 *      QString valeurPlusProche = GetVarNeerer ( sect, varn, datePlusProche);
 *  \todo Pour améliorer débuggage utiliser Var_Code
*/
//-----------------------------------------------------------------------------------------------------------------
QString CDevilCrucible::GetVarNeerer (QString &section, QString &variable, QString &dateSel)
{  QStringList valeurs          = GetVar (section, variable);
   QString     valeurPlusProche = GetVarNeerer ( valeurs, dateSel);
   QStringList ret = QStringList::split( "/;/" , valeurPlusProche);
   if (ret.count()>=2) return ret[1];
   else                return QString::null;
}

//------------------------------------------ GetVarNeerer --------------------------------------------------------
/*! \brief Récupère toutes la valeurs de la variable la plus poche de la date passée en entrée
 * \param QStringList     les valeurs datées (renvoyées par GetVar)
 * \param QString         la date voulue en format String ISO
 * \return QString de la valeurs datée au format :   DATE/;/VALEUR
 * EXEMPLE :
 *      QString sect = "VAR Poids";
 *      QString varn = "Poids";
 *      QStringList valeurs = GetVar (sect, svarn);
 *      QString datePlusProche = "2007-09-23T13:00:00"
 *      QString valeurPlusProche = GetVarNeerer ( valeurs, datePlusProche);
 *  \todo Pour améliorer débuggage utiliser Var_Code
*/
//-----------------------------------------------------------------------------------
QString CDevilCrucible::GetVarNeerer (QStringList &valuesList, QString &dateSel)
{if (valuesList.count() != 0 )
 {long          secondsToVar;
  long          secondsToRecent;
  QString       mostRecentVal = "";
  QString       sDate = dateSel.stripWhiteSpace();
  QDateTime     varQDate;
  QDateTime     dt = QDateTime::fromString( sDate , Qt::ISODate );   // DATE VISITE

  if (valuesList.count() == 1)
   {    //varDateVal = QStringList::split( "/;/", values[0]);
        //return varDateVal[1];
        return valuesList[0];
   }
  // trie de façon ascendante par la date (valeurVar[0] = la plus ancienne)
  valuesList.sort();

  // récupère dans varDate la première date comme référence la plus ancienne
  QStringList varDateValList    = QStringList::split( "/;/", valuesList[0]);
  QDateTime   varQDateRecent    = varQDateRecent.fromString(varDateValList[0] , Qt::ISODate);
  mostRecentVal                 = valuesList[0];        // au cas où il n'y aurait qu'une seule valeur...

  for (int i = 1; i < (int)valuesList.count(); ++i)
    {   varDateValList = QStringList::split( "/;/", valuesList[i] );
        varQDate       = varQDate.fromString(varDateValList[0], Qt::ISODate);

        // Compare le nombre de secondes entre DATE VISITE et les deux dates
        secondsToVar    = varQDate.secsTo(dt);
        secondsToRecent = varQDateRecent.secsTo(dt);
        if (secondsToVar < 0)         secondsToVar          = secondsToVar * (-1);
        if (secondsToRecent < 0)      secondsToRecent       = secondsToRecent * (-1);

    if (secondsToVar < secondsToRecent)
        { mostRecentVal  = valuesList[i];
          varQDateRecent = varQDate;
        }
     } // Fin  for

  return mostRecentVal;
 } // Fin if count > 0
 return "";
}


//------------------------------------------ GetVar ---------------------------------------------------------------
/*! \brief Récupère toutes les valeurs de la variable incluse dans section section_var de la rubrique Terrain
    \todo Utiliser Var_Code pour améliorer le débuggage
*/
// ENTREE :
//      section_var     : section
//      nalme_var       :le nom de la variable
// SORTIE :
//      QStringList des valeurs triés en ascendant des valeurs
// FORMAT DES DONNEES :
//      DATE/;/VALEUR
//
//  EXEMPLE :
//      QString sect = "VAR Poids";
//      QString varn = "Poids";
//      QStringList valeurs = GetVar (&sect, &varn);
//-----------------------------------------------------------------------------------------------------------------
QStringList CDevilCrucible::GetVar (QString &section_var, QString &name_var)
{// recuperer données soit dans liste cache soit sur disque
 QString   str_data    = m_pMB->GetDataFromRubList( GetIDCurrentDoc(TR("Terrain")) );
 if (str_data.length()==0)              return QString(" ");
 char *pt = (char*) (const char*) str_data;

 //........... effacer les anciennes tables de variables ................
 //
 QString       strTab[10];
 QString      section   = "";
 QString     var_name   = "";

 // char     *prop_section = 0;       // permet de noter le pointeur sur la section propriete renouvelable/intercurent des produits
 QStringList  valeurVar;        // récupère toutes les valeurs de VAR_name ainsi que leur date séparées par /;/

 while((pt = m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
    {//.................................. VAR ........................................................
     if (section == section_var )                  // à ce stade pt pointe au debut de la nouvelle ligne
        {int        n_var      = 0;
         int   pos_var_to_get  = -1;
         while (*pt && *pt != '[')
               {var_name = "";
                pt   = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &strTab[0], &strTab[1], &strTab[2], &strTab[3], &strTab[4], &strTab[5], &strTab[6], &strTab[7], &strTab[8]);
                //...................... DEFINITION DE VARIABLE .....................................
                //                       contruire le dialogue des variables
                if (var_name==TR("Modèle"))
                   {if (pos_var_to_get == -1 && strTab[1] == name_var) pos_var_to_get = n_var;   // noter la position de la variable
                    n_var++;
                   } // fin Modèle
                 //................ VALEURS DATEES DE VARIABLE ........................................
                 else if (pos_var_to_get != -1 && var_name !="" &&  strTab[pos_var_to_get] != "") // var_name = la date
                   {// Récupère les valeurs et les dates de la variable demandée dans les listes
                    valeurVar.append(var_name+"/;/"+strTab[pos_var_to_get]);
                   }
               } // end while (*pt && *pt != '[')
         section="";
        } // end if section_VAR

    } // end while
 return valeurVar;
}

//----------------------------- Func_Extract_VAR --------------------------------------
/*! \brief Extrait les variables selon le modèle demandé.
 *  \todo Utiliser Var_Code pour améliorer débuggage.
*/
//   [VAR Constantes de Base]
//      Modèle = Date, Pouls, 31536000, 130, 40, pps, Tachycardie, Bradycardie
//      Modèle = Date, SAO2, 31536000, 0, 92, %, , Hypoxie
//      Modèle = Date, Temp, 31536000, 40.5, 35, ?, Hyporthermie, Hyperthermie
//   [VAR Poids]
//      Modèle = Date, Poids,  31536000, 100, 50, Kg(s), Obésité, Amaigrissement
//      Modèle = Date, Taille, 31536000, 0, 0, cm(s),,
//
//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE ,   0  , TOUTES )}}
//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , -24  , PREMIERE )}}
//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , +24  , DERNIERE )}}
//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , +-24 , PLUS PROCHE )}}

QString CDevilCrucible::Func_Extract_VAR(QString &section_VAR, QString &name_VAR, QString &date_deb, QString &date_end, QString &date_intervalle)
{QString   resolvToken = "";

 section_VAR = section_VAR.stripWhiteSpace();
 name_VAR    = name_VAR.stripWhiteSpace();
 date_deb    = date_deb.stripWhiteSpace();
 date_end    = date_end.stripWhiteSpace();
 date_intervalle = date_intervalle.stripWhiteSpace().lower();

 //..................... récupère les valeurs et leurs dates ......................................
 QStringList valeurVar = GetVar(section_VAR, name_VAR);

 //..................... recuperer la date du document ............................................
 QString str_date = GetDateOfActiveRubrique();

//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE ,   0  , TOUTES )}}
//              Insère un tableau avec toutes les valeurs

//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , -24  , PREMIERE )}}
//              Insère la valeur chronologiquement la plus ancienne

//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , +24  , DERNIERE )}}
//              Insère la valeur chronologiquement la plus récente (voir futuriste...)

//      {{:: Extract_VAR (VAR Constantes de Base , SAO2 , DATE VISITE , +-24 , PLUS PROCHE )}}
//              Insère la valeur la plus proche (à la seconde près) de la date qu'elle soit antérieure ou postérieure à l'observation

  if (!valeurVar.isEmpty ())
   {
     QString            varDateStr;
     QDateTime          varDate;
     QDateTime          varDateMostRecent;
     QStringList        varDateVal;  // en  0 -> date ; en 1 -> valeur de la variable

     if (date_intervalle == "toutes")                                                           // Testé = OK
      { QString tableau;
        // tri les valeurs selon leur date  -- tri ascendant
        valeurVar.sort();

        // crée l'entete du tableau
        tableau =   "<table border=1 cellpadding=2 cellspacing=2>";
        tableau +=  "<tr><td><center><b>Date</b></center></td><td><center><b>";
        tableau +=  name_VAR + "</b></center></td></tr>";

        // insère les valeurs retrouvées
        for( int i = 0; i < (int)valeurVar.count() ; ++i)
         {
          varDateVal = QStringList::split( "/;/", valeurVar[i] );
          varDateVal[0].replace("T","&nbsp;&nbsp;");
          tableau += "<tr><td>"+varDateVal[0]+"</td><td>" +varDateVal[1] + "</td></tr>";
         }

        // Fin du tableau
        tableau += "</table><br /><br />";
        resolvToken = tableau;
      }
     else if (date_intervalle == TR("derniere"))                                                    // AMELIORABLE
      { valeurVar.sort();
        varDateVal = QStringList::split( "/;/", valeurVar.last () );
        resolvToken = varDateVal[1];
      }
     else if (date_intervalle == TR("premiere"))                                                   // AMELIORABLE
      { valeurVar.sort();
        varDateVal = QStringList::split( "/;/", valeurVar[0] );
        resolvToken = varDateVal[1];
      }
     else if (date_intervalle == TR("plus proche"))                                                 // Testé = OK
          { /*QString mostRecent = GetVarNeerer( valeurVar , str_date );
            varDateVal = QStringList::split( "/;/", mostRecent );
            resolvToken = varDateVal[1];*/
            resolvToken = GetVarNeerer ( section_VAR, name_VAR, str_date);
          }
  } // Fin si des valeurs ont été trouvées
  else
  { resolvToken = TR("Pas de valeur trouvée pour ") + name_VAR; }

  return    resolvToken;
}

//--------------------------- Func_Extract ---------------------------------------------------
/*! \brief Extrait une partie du texte du document selon le modèle demandé */
QString CDevilCrucible::Func_Extract(QString &doc_type, QString &tag_deb, QString &tag_end, QString &find_to, QString &replace_by)
{QString   resolvToken    = " ";
 const char* pt_doc       = 0;
 char*       deb          = 0;
 char    *debEndMotif     = 0;
 QString     var_nameRech = "";
 QString     var_nameRepl = "";
 QString     str_data     = "";
 doc_type   = doc_type.stripWhiteSpace();
 tag_deb    = tag_deb.stripWhiteSpace();
 tag_end    = tag_end.stripWhiteSpace();
 find_to    = find_to.stripWhiteSpace();
 replace_by = replace_by.stripWhiteSpace();
 if (doc_type.length()>0)
    {str_data    = m_pMB->GetDataFromRubList( GetIDCurrentDoc(doc_type) );       // recuperer données soit dans liste cache soit sur disque
     if (str_data.length()==0)              return resolvToken;
     pt_doc      = (char*) (const char*) str_data;
    }
 if (tag_deb.length()) deb = HtmlFind(pt_doc, tag_deb);
 if (deb && tag_end.length())
    {HtmlFind(deb, tag_end, &debEndMotif);
     if (debEndMotif)
        {resolvToken.setLatin1 (deb, debEndMotif - deb);
         CHtmlTools::HtmlToAscii(resolvToken);
         if ( find_to.length())
            {if ( resolvToken.find(find_to,0,TRUE) != -1)
                {resolvToken = replace_by;
                }
             else                                         resolvToken = "";
            }
         resolvToken.replace("\n","<br />");       // Remplace les sauts de lignes ASCII par les sauts de ligne HTML
         resolvToken.replace("\r","");             // pour les puristes...resolvToken=resolvToken.stripWhiteSpace();
        }
     }
 return resolvToken;
}

//-------------------------- extractArgList -------------------------------------------
/*! \brief non documenté */
long CDevilCrucible::extractArgList(QStringList &arg_list, QString arg_str)
{char   *deb  = (char*)(const char*) arg_str;
 char   *pt   = deb;
 long    len  = 0;
 if (pt==0)                           return -1;
 while(pt && *pt)
    {if (pt[0] == '\\')
        {pt += 2;
        }
     else if (pt[0] == ',')
        {
         len =  pt-deb;
         ++pt;
         if (len==0) arg_list.append( "" );
         else
           {QString s = QString::fromAscii(deb, len );
                    s = s.replace("\\\\","!°!;:");
                    s = s.remove("\\");
                    s = s.replace("!°!;:","\\");
            arg_list.append( s);
           }
         deb = pt;
        }
     else
        {++pt;
        }
    }
 len =  pt-deb;
 if (len==0) arg_list.append( "" );
 else
    {QString s = QString::fromAscii(deb, len );
             s = s.replace("\\\\","!°!;:");
             s = s.remove("\\");
             s = s.replace("!°!;:","\\");
     arg_list.append( s);
    }
 return 1;
}

//--------------------=-------- findLastDelimiter -----------------------------------------------
/*! \brief Pour la récursivité. */
long CDevilCrucible::findLastDelimiter(QString &str, char delimiter)
{char *deb = (char*)(const char*) str;
 char *pt  = deb;
 if (pt==0)                           return -1;
 while(pt && *pt)
    {if (pt[0] == '\\') pt += 2;
     else
        {if (pt[0] == delimiter)      return pt - deb;
         ++pt;
        }
    }
                                      return -1;
}

//---------------------------- ResolvTokenSyntax -------------------------------------------
/*! \brief non documenté */
int CDevilCrucible::ResolvTokenSyntax(QString &resolvToken, QString &token)
{
 resolvToken         = "";

 //char*        pt_deb = 0;
 //char*        pt_end = 0;
 char*        pt     = 0;
 const char*  pt_doc = 0;
 char*        deb    = 0;
 //int              id = -1;

 char        *debEndMotif = 0;
 QString     section      = "";
 QString     var_name     = "";
 QString     var_nameRech = "";
 QString     var_nameRepl = "";
 QString     str_data     = "";
 QString     val1, val2, val3, val4, val5;

 CHtmlTools::HtmlToAscii(token);
 pt = (char*)(const char*) token;

 while((pt = m_pMB->Param_GotoNextSection(pt, 0, &section)) && *pt)
    {//.................................. ATCD ........................................................
     if (section=="GET_SQL")
        {
        }
     else if (section=="GET_TEXT")
        {//  {{[GET_TEXT]
         //             Rubrique=Observation
         //             Debut apres = Certificat                :  u.
         //             Fin Avant = .t
         //  }}

         //  {{[GET_TEXT]
         //                Rubrique=Observation
         //                Debut apres = Circonstances :
         //                Fin Avant = Heure d'Arrivée SU:
         //                Rechercher = AT
         //                Remplacer = AT}}

         while (*pt && *pt != '[') // tant que pas au debut d'une nouvelle section
                {val1=""; val2=""; val3=""; val4=""; val5="";
                 pt_doc = 0;
                 deb    = 0;
                 pt     = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &val1 );
                 if (var_name== TR("Rubrique"))
                    {if (val1== TR("Observation"))
                        {str_data = m_pMB->GetDataFromRubList(GetIDCurrentDoc(TR("Observation")));       // recuperer données soit dans liste cache soit sur disque
                         pt_doc   = str_data;
                        }
                    }
                 pt  = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &val2 );
                 pt  = m_pMB->Param_ExtraireNextValeurs(pt, var_name, &val3 );
                 pt  = m_pMB->Param_ExtraireNextValeurs(pt, var_nameRech, &val4 );
                 pt  = m_pMB->Param_ExtraireNextValeurs(pt, var_nameRepl, &val5 );
                 if (val2!="") deb = HtmlFind(pt_doc, val2);
                 if (deb && val3!="")
                    {HtmlFind(deb, val3, &debEndMotif);
                     if (debEndMotif)
                        {resolvToken.setLatin1 (deb, debEndMotif-deb);
                             CHtmlTools::HtmlToAscii(resolvToken);
                             if ( (var_nameRech== TR("Chercher")||var_nameRech== TR("Rechercher")) && var_nameRepl== TR("Remplacer") )
                                {if (resolvToken.find(val4,0,TRUE)!=-1) resolvToken = val5;
                                 else                                   resolvToken = "";
                                }
                         resolvToken.replace("\r\n","<br>");
                         resolvToken=resolvToken.stripWhiteSpace();
                        }
                    }
                }
         section="";
        }
    } // end while((pt=m_pCMoteurBase->Param_GotoNextSection(pt, 0, &section)) && *pt)
 return 1;
}

//------------------------ HtmlFind ------------------------------------------
/*! \brief RETOUR : pointeur dans le texte après le tag de fin du motif trouvé.  ENTREE : char **debMotif pointeur de pointeur qui si est donné est positionné sur. 
 *  {{[GET_TEXT]?//             Rubrique=Observation?//             Debut apres = Certificat                     :  u.?//             Fin Avant = .t?//          }}
*/
char* CDevilCrucible::HtmlFind(const char* pt_in, QString motif, char **debMotif /*=0*/)
{if (pt_in==0) return 0;
 if (debMotif) *debMotif = 0;
 //.............. si il faut juste chercher la fin de la ligne ..................................
 char* pt = (char*) pt_in;
 if (motif=="FinDeLigne"||motif=="EOL")
    {while(*pt)
           {if (pt[0]=='<' && pt[1]=='b'&& pt[2]=='r')
               {int len = 0;
                if (pt[3]=='>')                                 len = 4;  // variante
                if (pt[3]==' '&& pt[4]=='/' && pt[5]=='>')      len = 6;  // variante
                if (len)
                   {if (debMotif) *debMotif = pt;
                    return pt+len;
                   }
               }
            ++pt;
           }
     return 0;
    }

 //.............. nettoyer le motif à comparer de tous les trucs non signifiant .................
 CHtmlTools::HtmlToAscii(motif);
 motif.remove((char)0xA0);
 motif.remove(' ');
 motif.remove('\t');
 motif.remove('\r');
 motif.remove('\n');
 int   carToCompareInMotif = 0;
 int   len_motif           =  motif.length();
 while (*pt)
{if (*pt=='<')
        {int len = 0;
     if (pt[0]=='<' && pt[1]=='b'&& pt[2]=='r')
            {if (pt[3]=='>')                                 len = 4;  // variante  <br>
             if (pt[3]==' '&& pt[4]=='/' && pt[5]=='>')      len = 6;  // variante  <br />
        }
         //.......... si tag  ou  la comparaison s'arrete car plus valide ....................
     if (len)
        {pt += len;
                 carToCompareInMotif=0;
                 if (debMotif) *debMotif = 0;
            }
     else
         //.............. le tag est à sauter ...........................................................
        {pt = IfHtmlTagJumpIt(pt);          // sauter tag html
        }
        }
  else
        {    char car = pt[0];
         if (car==' '||car=='\r'||car=='\n'||car=='\t'||car==(char)0xA0)                                      // sauter caractères non significatifs ||car=='\t'
            {++pt;
            }
         else //................... là on peut comparer ...............................................
            {if (car==(char)0xA0) {car = ' '; }
                 if (debMotif && carToCompareInMotif==0) *debMotif = pt;
                 if (pt[0]=='&')   //............. convertir les caractères codés .........................
                    {if      (pt[1]=='g'&&pt[2]=='t'&&pt[3]==';')                             {car = '>';  pt += 4;}
                     else if (pt[1]=='l'&&pt[2]=='t'&&pt[3]==';')                             {car = '<';  pt += 4;}
                     else if (pt[1]=='a'&&pt[2]=='m'&&pt[3]=='p'&&pt[4]==';')                 {car = '&';  pt += 5;}
                     else if (pt[1]=='n'&&pt[2]=='b'&&pt[3]=='s'&&pt[4]=='p'&& pt[5]==';')    {car = ' ';  pt += 6;}
                     else if (pt[1]=='q'&&pt[2]=='u'&&pt[3]=='o'&&pt[4]=='t'&& pt[5]==';')    {car = '\"'; pt += 6;}
                     else                                                                     {car = *pt;  pt += 1;}
                    }
                 else
                    {++pt;
                    }
             //car_t = motif[carToCompareInMotif].latin1();
                 if (car == motif[carToCompareInMotif])
                    {++carToCompareInMotif;
                     if (carToCompareInMotif >= len_motif) return pt;
                    }
                 else
                    {carToCompareInMotif=0;
                     if (debMotif) *debMotif = 0;
                    }
                }
        }
    }// end while (*pt && carToCompareInMotif < len_motif)
 return 0;
}

//--------------------------------- IfHtmlTagJumpIt ------------------------------------------
/*! \brief Saute le tag HTML. */
char* CDevilCrucible::IfHtmlTagJumpIt(char* pt)
{if (pt[0]=='<')
    {++pt;
     while (*pt)
        {if (pt[0]=='>') return pt +1;
         ++pt;
        }
    }
 return pt;
}


