#include "pisysdep.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include PIMENU_H
#include "picons.h"


//++
// Class	PIConsole
// Lib		PI
// include	picons.h
//
//   	Cette classe fournit une partie des services equivalentes
//	a une console. Les sorties stdout et stderr peuvent etre 
//	redirigees sur un objet *PIConsole* (Voir "PIApplication").
//	La classe offre aussi la possibilite de gestion d'un ScrollBar,
//	ainsi que la saisie et l'edition de commande, avec la gestion
//	d'un buffer de rappel.
//
//	* Bouton-1 : Envoie le message "PIMsg_Active". Permet aussi 
//	la selection d'un rectangle pour le copie/coller
//	* Bouton-2 : Effectue l'operation Copier/*Coller* sur la ligne
//	d'edition de commandes.
//	de commande (convention Xt)
//	* Bouton-3 : Affiche le menu des options d'affichage.
//
//	Gestion du clavier :
//	* <Alt>O : Affiche le menu des options d'affichage.
//	* <Alt>V : Copier/*Coller* Copie le texte depuis le buffer copier/coller
//	sur la ligne d'edition de commande.
//	* <Alt>A : Selectionne toute la fenetre pour *Copier*/Coller
//	* <Alt>L : Liste des commandes dans le tampon de rappel.
//
//	Commandes d'edition : 
//	* <Ctl>A : Debut de ligne.
//	* <Ctl>E : Fin de ligne.
//	* <Ctl>K : Effacement jusqu'en fin de ligne.
//	* <Ctl>C : Effacement de la ligne.
//	* Curseur gauche, droite : deplacement curseur
//	* Cursur Haut,bas : rappel de commande
//	* Backspace, del : effacement de caracteres
//	* <Return> : Validation
//--
//++
// Links	Parents
// PIBaseWdg
//--

// #define DEBUG_PICONS 1

//  Le menu qui va servir a changer de fontes, couleurs, ...
// de bouton-3
static PIMenu* opmc=NULL;
static int nb_opmc = 0;

static void ActivatePIConsOptionMenu(PIConsole* par)
{
  if (opmc)  delete opmc;
  opmc = NULL;

  opmc = new PIMenu(par, "OptCons", k_PopupMenu);
  opmc->AppendItem("SmallFont", 3101);
  opmc->AppendItem("NormalFont", 3102);
  opmc->AppendItem("LargeFont", 3103);
  opmc->AppendItem("25L x 132C", 3301);
  opmc->AppendItem("20L x 80C", 3302);
  opmc->AppendItem("10L x 80C", 3303);
  opmc->AppendItem("5L x 60C", 3304);
  opmc->AppendItem("Bkg White", 3201);
  opmc->AppendItem("Bkg Black", 3202);
  opmc->AppendItem("Black/Yellow", 3203);
  
  opmc->SetMsgParent((PIMsgHandler*)par);
  opmc->Show(); 
  
}

/*  --------------------------------------------------------------------------------- */
/*  ---------  Classe PIConsole : I/O (Ecriture/lecture) texte standard  ------------ */
/*  --------------------------------------------------------------------------------- */

static int CmdStrLen = 256;
static int PIVA_Select = 1 << 3;

//  Gestion du scroll-bar
static int Scb_Width = 14;


//++
// Titre	Constructeur
//--
//++
// PIConsole(PIContainer* par, const char* nom, PIMessage msg, \
//           int nl, int nc, int sx, int sy, int px, int py, bool scb)
//	Creation d'un objet PIConsole, avec "nl" lignes et  "nc" colonnes.
//	"sx,sy" indique la taille de la fenetre. Si "scb==true", un
//	ScrollBar est cree associe a l'objet PIConsole.
//--

/* --Methode-- */
PIConsole::PIConsole(PIContainer* par, const char* nom, PIMessage msg,
                     int nl, int nc, int sx, int sy, int px, int py, bool scb) :
PIBaseWdg(par, nom, ((scb)?sx-Scb_Width:sx), sy, px, py) 
{
mNL = (nl > 10) ? nl : 10;
mNC = (nc > 30) ? nc : 30;
mText = new char[mNL*mNC];
mAtt = new unsigned char[mNL*mNC];
mLPo = new int[mNL];
mStrBuf = new char[mNC+1];
mCmdStr = new char[CmdStrLen+1];
mCCP = mCLC = 0;
mDCOff = 0;
mScb = NULL;

SetMsg(msg);

int i;
for(i=0; i<mNL; i++) {
  mLPo[i] = i*mNC;
  ClearLine(i);
  }

mOffL = 0;
mNCmdL = 0;
mCmd = "";

// Pile de rappel des commandes 
mRStr = NULL;
mNComm = NMxRecall = 0;
mRCindx = mRindx = 0;
mChangeFg = false;

// Copier/coller
mCPBuffer = NULL;
mCPBufLen = 0;
mCPbeginC =  mCPendC = -1;
mCPbeginL = mCPendL = -1;

SetPrompt("Cmd> ");
ClrCmd();
SelectFont();
SelFgBgCol();
mCurL = mNL-mWSzL-1; 
mCurC = 0;


// Pour ajouter un scroll-bar
AssocScrollBar(par, nom, scb, sx, sy, px, py);


ActivateKeyboard();  // Gestion de l'edition 
ActivateButton(1);   //  Action selection / copier - coller 
ActivateMove(1);     //      "    "     "  
ActivateButton(2);   //      "    "     "  
// ActivateButton(3);   Pour afficher le menu des options (Fontes/couleur)
Manage();
return;
}

/* --Methode-- */
PIConsole::~PIConsole()
{
delete[] mText;
delete[] mAtt;
delete[] mLPo;
delete[] mStrBuf;
delete[] mCmdStr;
if (mScb) delete mScb;
if (opmc) { delete opmc;  opmc=NULL; }
}


//++
// Titre	Méthodes
//	Seule les méthodes les plus usuelles sont décrites ici. Se reporter
//	au fichier .h pour la liste complete des méthodes
//	le type "enum PIVAType" est utilise pour definir les attributs
//	graphiques du texte affiche dans l'objet PIConsole. Les attributs 
//	de texte et de couleur peuvent etre combine a l'aide de l'operateur
//	ou logique (|)
//|  PIVA_Def : Attribut par defaut
//|  PIVA_Bold : Caracteres gras
//|  PIVA_Ital : Caracteres italiques
//|  PIVA_Reverse : Caracteres en video inverse
//|  PIVA_DefCol : Couleur par defaut
//|  PIVA_Red : couleur rouge 
//|  PIVA_Blue : Couleur bleue 
//|  PIVA_Green : Couleur verte 
//|  PIVA_Yellow : couleur jaune
//|  PIVA_Magenta : couleur mauve 
//--  

//++
// void  AddChar(char c, unsigned char va= PIVA_Def, bool ref=true)
//	Ajout d'un caractere a la position courante. si "ref==true", 
//	la fenetre est rafraichie. le caractere newline declenche
//	un retour a la ligne.
// void  AddStr(const char* strg, unsigned char va= PIVA_Def, bool ref=true);
//	Ajout d'une chaine de caracteres a partir de la position courante. 
//	si "ref==true", la fenetre est rafraichie. le caractere newline declenche
//	un retour a la ligne.
// void  AcceptCmd(bool acc=true, int nrecmx=20)
//	Active/desactive l'edition de commandes. "nrecmx" indique la profondeur
//	du tampon de rappel de commandes. Le message associe a l'objet PIConsole
//	avec la qualificatif "PIMsg_OK" est envoye lors de la validation de la
//	commande.
// string  GetCmdString()
//	Renvoie la chaine de caractere correspondant a la ligne editee.
//--

/* --Methode-- */
void PIConsole::AddChar(char c, unsigned char va, bool ref)
{
int l1, l2;
if (mOffL != 0)  // Decalage d'affichage precedant -> Tout rafraichir 
  { l1 = mNL-mWSzL-1; l2 = mNL-1;  SetDispOffset(-1); }
else l1 = l2 = mCurL; 
if (c == '\n') {
  mCurC = 0;
  mCurL++;   
  }
else {
  mText[mLPo[mCurL]+mCurC] = c;
  mAtt[mLPo[mCurL]+mCurC] = va;
  if (++mCurC == mNC) {mCurC = 0;  mCurL++; }
  }

if (mCurL >= (mNL-mNCmdL) ) { // Il faut scroller 
  ScrollUp();  
  mCurL = mNL-mNCmdL-1;
  l1 = mNL-mWSzL-1; 
  }
if (l2 < mCurL) l2 = mCurL;
if (ref) DisplayLines(mWGrC, l1, l2);
return;
}


/* --Methode-- */
void PIConsole::AddStr(const char* strg, unsigned char va, bool ref)
{
int l1, l2;
if (mOffL != 0)  // Decalage d'affichage precedant -> Tout rafraichir 
  { l1 = mNL-mWSzL-1; l2 = mNL-1;  SetDispOffset(-1); }
else l1 = l2 = mCurL; 

char c;
while ( (c = *strg) != '\0') {
  if ( c != '\n' ) {
    mText[mLPo[mCurL]+mCurC] = c;
    mAtt[mLPo[mCurL]+mCurC] = va;
    if (++mCurC == mNC) {mCurC = 0;  mCurL++; }
    }
  else { mCurL++; mCurC = 0; } 
  if (mCurL >= (mNL-mNCmdL) ) { // Il faut scroller 
    ScrollUp();  
    mCurL = mNL-mNCmdL-1;
    l1 = mNL-mWSzL-1; 
    }
  strg++;
  }

if (l2 < mCurL) l2 = mCurL;
if (ref) DisplayLines(mWGrC, l1, l2);
return;
}

/* --Methode-- */
void PIConsole::SelectFont(PIFontSize sz)
{
int asc, desc;
mWGrC->SelFont(sz);
mFTaille = sz;
mFHaut = mWGrC->GetFont().GetFontHeight(asc, desc);
mFLarg = (int)mWGrC->CalcStringWidth("ABCDEFGHIJ")/10;
mWSzL = YSize()/mFHaut - 1;
mWSzC = XSize()/mFLarg - 2;
mOffL = 0;
if (mScb) {
  mScb->SetMinMax(0, NbLines()-1);
  mScb->SetSlSize(WindNbLines());
  mScb->SetValue(0);
  }
}


/* --Methode-- */
void PIConsole::AutoSize(int nl, int nc)
{
if ( (nl < 1) || (nc < 1) ) {
  nl = mWSzL;   nc = mNC;
  }
if (nc > mNC) nc = mNC;
int dsz[2];
dsz[0] = (nc+2)*mFLarg - XSize();
dsz[1] = (nl+1)*mFHaut - YSize();
Send(Msg(), PIMsg_ResizeRequest, dsz);

mWSzL = YSize()/mFHaut - 1;
mWSzC = XSize()/mFLarg - 2;
/*
if (mScb) {
  mScb->SetMinMax(0, NbLines()-1);
  mScb->SetSlSize(WindNbLines());
  mScb->SetValue(0);
  }
  */
mOffL = 0;
}

/* --Methode-- */
void PIConsole::PreferedSize(int& sx, int& sy)
{
sx = (mNC+2)*mFLarg;
sy = (mWSzL+1)*mFHaut;
return;
}

/* --Methode-- */
void PIConsole::AcceptCmd(bool acc, int nmxrec)
{
int l1, l2;
if (acc) {
  if (mNCmdL)  return;  // Pas de changement d'etat
  if (mCurL == (mNL-1) ) { l1 = mNL-mWSzL-1; ScrollUp(); mCurL = (mNL-2); }
  else l1 = mNL-1;
  mNCmdL = 1;
  ClrCmd();
  UpdCmdLine();
  l2 = mNL-1;
  if (nmxrec > 0) {
    NMxRecall = (nmxrec > 5) ? nmxrec : 5;
    if (mRStr)  delete[] mRStr;
    mRStr = new string[NMxRecall];
    mNComm = mRCindx = mRindx = 0;
    mRStr[0] = "";
    }
  }
else {
  if (mNCmdL == 0)  return;  // Pas de changement d'etat
  mNCmdL = 0;
  ClearLine(mNL-1);
  ClrCmd();
  l1 = l2 = mNL-1;
  delete[] mRStr;
  mRCindx = mRindx = 0;
  }
DisplayLines(mWGrC, l1, l2);
return;
}

/* --Methode-- */
void PIConsole::SetPrompt(const char* prompt)
{
strncpy(mPrompt, prompt, 31);  
mPrompt[31] = '\0';
return;
}

/* --Methode-- */
void PIConsole::SetDispOffset(int ldeb)
{
if (ldeb < 0)    { 
  if (mScb) mScb->SetValue(0); 
  mOffL = 0;
  }
else mOffL = (ldeb < mNL-mWSzL-1) ? ldeb : mNL-mWSzL-1;
}


/* --Methode-- */
void PIConsole::Resize()
{
mWSzL = YSize()/mFHaut - 1;
mWSzC = XSize()/mFLarg - 2;
if (mScb) {
  mScb->SetMinMax(0, NbLines()-1);
  mScb->SetSlSize(WindNbLines());
  mScb->SetValue(0);
  }
mOffL = 0;
}

/* --Methode-- */
void PIConsole::Draw(PIGraphicGen* g, int /*x0*/, int /*y0*/, int /*dx*/, int /*dy*/)
{
EraseWindow();
DisplayLines(g, 0, mNL);
}


/* --Methode-- */
void PIConsole::Keyboard(int key, PIKeyModifier kmod)
{

if (kmod == PIKM_Alt) {
  if (key == 'O' || key == 'o') {   // Menu des options 
    ActivatePIConsOptionMenu(this);
    return;
    }
  else if (key == 'A' || key == 'a') {  // Pour tout selectionner
    mCPbeginC = 0; mCPendC = XSize(); 
    mCPbeginL = 0;  mCPendL = YSize(); 
    SaveSelection();
    return;
    }
  else if (key == 'L' || key == 'l') {  // Pour lister le buffer de rappel
    int nmxrec = (mNComm < NMxRecall) ? mNComm : NMxRecall;
    if (nmxrec < 1)  return;
    AddStr("PIConsole - Command history : \n",  PIVA_Def, false);
    int ridx = mRCindx+nmxrec;
    int kk;
    char strg[64];
    for(kk=0; kk<nmxrec; kk++) {
      sprintf(strg, "%d- ", mNComm-nmxrec+kk+1);
      AddStr(strg, PIVA_Def, false);
      AddStr(mRStr[ridx%nmxrec].c_str(), PIVA_Def, false);
      AddChar('\n', PIVA_Def, false);
      ridx++;
      }
    AddChar('\n', PIVA_Def, false);
    Refresh();
    return;
    }
  }

if (mNCmdL < 1)  return;

int k;
bool fgfk = false;

if (kmod == PIKM_Cntl) {
  switch (key) {
    case 1 :  // <Cntl> A  Debut de ligne
      mCCP = 0;
      break;
    case 3 :  // <Cntl> C  On efface la ligne
      mCCP = mCLC = 0;
      mCmdStr[mCLC] = '\0';
      mRindx = mRCindx;
      mChangeFg = true;
      break;
    case 5 :  // <Cntl> E  Fin de ligne
      mCCP = mCLC;
      break;
    case 11 :  // <Cntl> K  On efface jusqu'en fin de ligne
      mCLC = mCCP;
      break;
    }
  }

else if (kmod == PIKM_Alt) {
  if (key == 'V' || key == 'v') RequestSelection();  // Pour coller (copier/coller)
  }

else if (kmod == PIKM_Blank) {
  switch (key) {
    case PIK_Return :    
    case PIK_Enter :
      SendCmd();
      break;

    case PIK_BackSpace :
    case 127 :    // Touche del
    case 8 :      // Touche backspace
      if (mCCP > 0)  { 
        for(k=mCCP-1; k<mCLC-1; k++)  mCmdStr[k] = mCmdStr[k+1];
        mCmdStr[mCLC-1] = ' ';   mCCP--;  mCLC--;
        mChangeFg = true;
        }
      //      printf("Keyb()-Debug- Backspace CCP,CLC= %d %d CmdStr= %s (L=%d) \n", mCCP, mCLC, 
      //      mCmdStr, strlen(mCmdStr));
      break;
      
    case PIK_Up :
    case PIK_Down :
//      printf("Keyb()-Debug- CursorUp/Down(%d)  Index= %d %d  \n", key, mRindx, mRCindx);
      if (mNComm > 0) {
        if (mChangeFg) { 
          mCmdStr[mCLC] = '\0';
          mRStr[mRCindx] = mCmdStr;
          mChangeFg = false;
	  }
        int nmxrec = ((mNComm+1) < NMxRecall) ? mNComm+1 : NMxRecall;
        if (key == PIK_Up) { 
           mRindx --;
           if (mRindx < 0) mRindx += nmxrec;
	   }
        else mRindx++;
        int ridx = mRindx%nmxrec;
        mCLC = mCCP = mRStr[ridx].length();
        for(k=0; k< mCLC; k++)  mCmdStr[k] =  mRStr[ridx][k];
        mCmdStr[mCLC] = '\0';      
        }      
      break;

    case PIK_Left :
      if (mCCP > 0)  mCCP--;
      //      printf("Keyb()-Debug- Left CCP,CLC= %d %d  \n", mCCP, mCLC);
      break;
    case PIK_Right :
      if (mCCP < mCLC)  mCCP++;
      //      printf("Keyb()-Debug- Right CCP,CLC= %d %d  \n", mCCP, mCLC);
      break;
    default :
      fgfk = true;
      break;
    }
  }
if ( ( fgfk && (kmod == PIKM_Blank) ) || (kmod == PIKM_Shift)  ) CmdAddChar(key);
UpdCmdLine();
// DisplayLines(mNL-1, mNL-1);
return;
}


/* --Methode-- */
void PIConsole::But1Press(int x, int y)
{
Send(Msg(), PIMsg_Active);
AssignKeyboard();
mWGrC->SelGOMode(PI_GOXOR);
mWGrC->SelForeground(PI_Grey);
mCPbeginC = x;  mCPbeginL = y;
mCPendC = mCPendL = -1;
} 

/* --Methode-- */
void PIConsole::Ptr1Move(int x, int y)
{
if (mCPendC >= 0) mWGrC->DrawBox(mCPbeginC, mCPbeginL, mCPendC-mCPbeginC, mCPendL-mCPbeginL);
mCPendC = x;  mCPendL = y;
mWGrC->DrawBox(mCPbeginC, mCPbeginL, mCPendC-mCPbeginC, mCPendL-mCPbeginL);
}
 
/* --Methode-- */
void PIConsole::But1Release(int x, int y)
{
if ( (mCPendC >= 0) && (mCPbeginC >= 0) ) { 
  mWGrC->DrawBox(mCPbeginC, mCPbeginL, mCPendC-mCPbeginC, mCPendL-mCPbeginL);
  mCPendC = x;  mCPendL = y;
  }
else { 
  mCPbeginC =  mCPendC = -1;
  mCPbeginL = mCPendL = -1;
  }
mWGrC->SelGOMode(PI_GOCopy);
mWGrC->SelForeground(mFgC);
SaveSelection();
} 

/* --Methode-- */
void PIConsole::But2Press(int x, int y)
{
RequestSelection();  // Pour recuperer la selection = coller (copier/coller)
} 

/* --Methode-- */
void PIConsole::But3Press(int x, int y)
{
ActivatePIConsOptionMenu(this);
}

/* --Methode-- */
void PIConsole::PasteSelection(unsigned int typ, void *pdata, unsigned int l)
{
if (typ != PICP_string) return;
int i;
char *pc = (char *)pdata;
for(i=0; i<l; i++) {
  if ((i > 0) && (pc[i] == '\n'))  SendCmd();
  else CmdAddChar(pc[i]);
}
UpdCmdLine();
}

/* --Methode-- */
void PIConsole::SelectionLost()
{
// printf("Debug PIConsole::SelectionLost() %lx (L=%d)\n",(long)mCPBuffer, mCPBufLen);
if (!mCPBuffer) return;
delete[] mCPBuffer;  
mCPBuffer = NULL;
mCPBufLen = 0;
mCPbeginC =  mCPendC = -1;
mCPbeginL = mCPendL = -1;
int i,j;
int l1,l2;
l1 = mNL+1; l2 = -1;
for(i=0; i<mNL; i++)   // On enleve le bit select a tous les caracteres 
  for(j=0; j<mNC; j++) 
    if ( mAtt[mLPo[i]+j] & PIVA_Select) {
      if (l1 > i)  l1 = i;
      if (l2 < i)  l2 = i;
      mAtt[mLPo[i]+j] &= ~PIVA_Select ;
    }

if (IsVisible() && l2 > -1)  DisplayLines(mWGrC, l1, l2);
return;
}

/* --Methode-- */
void PIConsole::SelectionTransferEnd()
{
// printf("Debug PIConsole::SelectionTransferEnd() -Rien- %lx (L=%d)\n",(long)mCPBuffer, mCPBufLen);
}


/* --Methode-- */
void * PIConsole::ProvideSelection(unsigned int& typ, unsigned int& len)
{
typ = PICP_string;
len = mCPBufLen;
return(mCPBuffer);
}

/* ----------------------------------------------------------------------------- */
/*    ---------------------- Methodes protected -------------------------        */
/* ----------------------------------------------------------------------------- */


/* --Methode-- 
void PIConsole::DebugPrint(int lp)
{
printf("PIConsole::DebugPrint() - NL,NC= %d %d - WSzL,C= %d %d Cur= %d %d\n", mNL, mNC, 
        mWSzL, mWSzC, mCurL, mCurC);
printf(" OffL, NCmdL= %d %d - mCCP, mCLC= %d %d - LPo= %d %d \n", 
        mOffL, mNCmdL, mCCP, mCLC, mLPo[mNL-2], mLPo[mNL-1]);

if (lp == 0)  return;
int i,j;
for(j=0; j<mNL; j++) {
  printf("Line[%d] (%d) = ", j, mLPo[j]);
  for(i=0; i<mNC; i++)  putchar(mText[mLPo[j]+i]);
  putchar('\n');
  }
return;
}
*/


/* --Methode-- */
void PIConsole::AssocScrollBar(PIContainer* par, const char* nom, bool scb, int sx, int sy, int px, int py)
{
if (!scb) {
  mScb = NULL;
  msgScb = 0;
  SetBinding(PIBK_fixed, PIBK_fixed, PIBK_fixed, PIBK_fixed);
  return;   
}

sx -= Scb_Width;
// SetSize(sx, sy);
SetBinding(PIBK_fixed, PIBK_fixed, PIBK_fixed, PIBK_fixed);
char name[128];
strcpy(name,"ScrollBar_");
strncat(name, nom, 127);
msgScb = Msg()+55;
mScb = new PIScrollBar (par, name, msgScb, kSDirDownUp, Scb_Width, sy, px+sx, py);
mScb->SetBinding(PIBK_free, PIBK_fixed, PIBK_fixed, PIBK_fixed);
  
mScb->SetMsgParent(this);
mScb->SetMinMax(0, NbLines()-1);
mScb->SetSlSize(WindNbLines());
mScb->SetValue(0);
mScb->ActivateDrag(true);
}

/* --Methode-- */
void PIConsole::ScrollUp()
{
int i,l,kpo;
l = mNL-1-mNCmdL;
kpo = mLPo[0];
for(i=0; i<mNL-mNCmdL-1; i++)   mLPo[i] = mLPo[i+1];
mLPo[l] = kpo;
ClearLine(l);
return;
}

/* --Methode-- */
void PIConsole::ClearLine(int l)
{
if ((l<0) || (l >= mNL)) return;
int j;
for(j=0; j<mNC; j++) {
  mText[mLPo[l]+j] = ' ';
  mAtt[mLPo[l]+j] = PIVA_Def;
  }
return;
}

/* --Methode-- */
void PIConsole::DisplayLines(PIGraphicGen* g, int l1, int l2)
{
int j,l,k,l0;
int x,y;

// DBG printf("PIConsole::DisplayLines(%d %d ) -> %d - %lx \n ", l1, l2, 
//            (int)IsVisible(), (long)XtWindow(XtWdg()));

if (! IsVisible() )   return;

int lva = -1;
int ccol, cfnt;

PIFontAtt vafat[8] = {PI_RomanFont, PI_BoldFont, PI_ItalicFont,
                      PI_RomanFont, PI_RomanFont, PI_BoldFont, 
                      PI_ItalicFont, PI_RomanFont};
PIColors fgc, bgc, dbgc;
PIColors col[6] = {PI_Black, PI_Red, PI_Blue, PI_Green, PI_Yellow, PI_Magenta};

g->SelForeground(mFgC);
g->SelBackground(mBgC);
fgc =  col[0] = g->GetForeground();
dbgc = bgc =  g->GetBackground();

int dx = mFLarg;
int dy = mFHaut/2;
int dxo2 = dx/2;
if (dxo2 > 3)  dxo2 = 3;
int dyo2 = dy/2;
if (dyo2 > 3)  dyo2 = 3;

g->DrawBox(dxo2, dyo2, (mNC+2)*mFLarg-2*dxo2, (mWSzL+1)*mFHaut-dyo2);

l0 = mNL-mWSzL-mOffL;
if (l0 < 0) l0 = 0;
if (l1 < l0) l1 = l0;
if (++l2 > mNL-mOffL) l2 =  mNL-mOffL;

for(l=l1; l<l2; l++) {
 y = (l-l0+1)*mFHaut+dy;
 k = 0;   x = dx;
 for(j=0; j<mNC; j++) {
   if (mAtt[mLPo[l]+j] != lva) {
     if (k > 0) {
       mStrBuf[k] = '\0';     
       g->DrawOpaqueString(x, y, mStrBuf);
       //     printf("l,J,K=%d %d %d - X,Y= %d %d - %s \n", l,j,k,x,y,mStrBuf);
       x += k*mFLarg;  k = 0;  
       }
     lva = mAtt[mLPo[l]+j];
     cfnt = lva & 15;   //  Index de fonte
     ccol = lva >> 4;   //  Index de couleur
     fgc = col[ccol];
     if (cfnt & PIVA_Select) 
       { bgc = PI_Grey;   g->SelFont(mFTaille, vafat[cfnt-PIVA_Select]); }
     else { bgc = dbgc; g->SelFont(mFTaille, vafat[cfnt]); }
     
     if (cfnt & PIVA_Reverse) { 
       g->SelForeground(bgc);
       g->SelBackground(fgc);
       }
     else  { 
       g->SelForeground(fgc);
       g->SelBackground(bgc);
       }
     }
   mStrBuf[k] = mText[mLPo[l]+j];
   k++;
   }
   if (k > 0) {
     mStrBuf[k] = '\0';     
     g->DrawOpaqueString(x, y, mStrBuf);
     //     printf("l,J,K=%d %d %d - X,Y= %d %d - %s \n", l,j,k,x,y,mStrBuf);
   }
 }

g->SelForeground(fgc);
g->SelBackground(dbgc);
return;
}

/* --Methode-- */
void PIConsole::UpdCmdLine()
{
//  char strg[260];  debug
int l1,l2;
l1 = l2 = mNL-1;
if (mOffL != 0)  // Decalage d'affichage precedant -> Tout rafraichir 
  { l1 = mNL-mWSzL-1; l2 = mNL-1;  SetDispOffset(-1); }

ClearLine(mNL-1);
int j1,j2,j,jj2;
j1 = strlen(mPrompt);
for(j=0; j<j1; j++) {
  mText[mLPo[mNL-1]+j] = mPrompt[j];
  mAtt[mLPo[mNL-1]+j] = PIVA_Def;
//  strg[j] = mPrompt[j];    debug
  }  
jj2 = mWSzC-j1;
if (mDCOff>=mCCP) mDCOff = 0;
int koff = ((mCCP-mDCOff) >= jj2) ? mCCP-(jj2/2) : mDCOff;
mDCOff = koff;
int kmx = mCLC-koff;
j2 = j1+kmx;
if (j2 > mWSzC)  j2 = mWSzC;
int k = koff;
for(j=j1; j<j2; j++) {
  mText[mLPo[mNL-1]+j] = mCmdStr[k];
  mAtt[mLPo[mNL-1]+j] = PIVA_Bold;
//  strg[j] = mCmdStr[k];   debug 
  k++;
  }  

// strg[j] = '\0';   Debug
// printf("UpdCmdLine() %d %d - %s (%d) \n [%d %d %d] %d\n", koff,kmx,strg,j1+mCCP-koff, mWSzC, j1, jj2, mCCP-(jj2/2));
mAtt[mLPo[mNL-1]+j1+mCCP-koff] = PIVA_Reverse;    // Pour le curseur
DisplayLines(mWGrC, l1, l2);
}

/* --Methode-- */
void PIConsole::ClrCmd() 
{ 
mCCP = 0; mCLC = 0; mDCOff = 0;
mCmdStr[0] = '\0'; 
}  

/* --Methode-- */
void PIConsole::SendCmd() 
{ 
  mCmdStr[mCLC] = '\0';
  //      printf("Keyb()-Debug- <CR/Enter> CmdStr= %s (L=%d)\n", mCmdStr, strlen(mCmdStr) );
  // Gestion de rappel des commandes 
  if (mCLC > 0) {
    mRStr[mRCindx] = mCmdStr;
    mRCindx++;  mNComm++;
    if (mRCindx >= NMxRecall)  mRCindx = 0;
    mRindx = mRCindx;
  }
  mChangeFg = false;
  mCmd = mCmdStr;
  ClrCmd();
  Send( Msg(), PIMsg_OK, NULL);
  return;
}

/* --Methode-- */
void PIConsole::CmdAddChar(int key)
{
  int k, kmx;
  if ( (mCCP < CmdStrLen) && isprint(key) ) {   // Ajout de caracteres
    if (mCCP < mCLC)  { // Insertion au milieu de la chaine
      kmx = (mCLC < CmdStrLen) ? mCLC : CmdStrLen-1;
      //      printf(" ++DBG++ C=%d L=%d MX=%d KMX=%d\n", mCCP, mCLC, CmdStrLen,kmx);
      for(k=kmx; k>mCCP; k--) mCmdStr[k] = mCmdStr[k-1];   mCLC = kmx+1;      
      }
    mCmdStr[mCCP] = key;  mCCP++; 
    if (mCCP > mCLC)  mCLC = mCCP;
    mChangeFg = true;
    }
}


/* --Methode-- */
void PIConsole::SelFgBgCol(PIColors fc, PIColors bc)
{
mFgC = fc; mBgC = bc;
SetBackgroundColor(bc);
}


/* --Methode-- */
void PIConsole::SaveSelection()
{
int i,j,k,l;
int l1, l2, nl, nc;

if (mCPbeginC > mCPendC) { j = mCPbeginC; mCPbeginC = mCPendC ; mCPendC = j; }
if (mCPbeginL > mCPendL) { j = mCPbeginL; mCPbeginL = mCPendL ; mCPendL = j; }

nc = (mCPendC-mCPbeginC)/mFLarg;
nl = (mCPendL-mCPbeginL)/mFHaut;
if (nl < 1) nl = 1;  // On selectionne au moins une ligne
// printf("*DBG* NC=%d NL=%d \n",nc, nl); 
l1 = mNL+1; l2 = -1;

if (mCPBuffer) {
  delete[] mCPBuffer;  
  mCPBuffer = NULL;
  mCPBufLen = 0;
  for(i=0; i<mNL; i++)   // On enleve le bit select a tous les caracteres 
    for(j=0; j<mNC; j++) 
      if ( mAtt[mLPo[i]+j] & PIVA_Select) {
        if (l1 > i)  l1 = i;
        if (l2 < i)  l2 = i;
        mAtt[mLPo[i]+j] &= ~PIVA_Select ;
      }
  }

// printf("**DBG** Save: C= %d %d  L = %d %d \n", mCPbeginC, mCPendC, mCPbeginL, mCPendL);


// On transforme la position sur la fenetre en position en caracteres ds les buffers

mCPbeginC = mCPbeginC/mFLarg-1;
mCPendC = (int) ( (float)mCPendC/(float)mFLarg-0.8 );
mCPbeginL = (int) ( (float)mCPbeginL/(float)mFHaut-0.5 ) ;
mCPendL = (int) ( (float)mCPendL/(float)mFHaut-0.5 ) ;
if (mCPendL <= mCPbeginL) mCPendL = mCPbeginL+1;  // Au moins une ligne selectionnee
// printf("..DBG.. C= %d %d  L = %d %d ", mCPbeginC, mCPendC, mCPbeginL, mCPendL);
j = mNL-mWSzL-mOffL;
mCPbeginL += j;  mCPendL += j;
// printf(" -> %d %d \n", mCPbeginL, mCPendL);

if (mCPbeginC < 0) mCPbeginC = 0;
if (mCPendC > mNC) mCPendC = mNC;
if (mCPbeginL < 0) mCPbeginL = 0;
if (mCPendL > mNL) mCPendL = mNL;

if ( (nc > 0) && (nl > 0) ) {
if (!ClaimSelection()) { 
  mCPbeginC = mCPendC = -1;
  mCPbeginL = mCPendL = -1;
  if (l2 > -1) DisplayLines(mWGrC, l1, l2);  
  return;
  }
  l = (mCPendC-mCPbeginC+1) * (mCPendL-mCPbeginL);
  if (l < 0) l = 0;
  mCPBufLen = l;
  mCPBuffer = new char[l+1];  
  k = 0;  
  for(j=mCPbeginL; j<mCPendL; j++) { 
    for(i=mCPbeginC; i<mCPendC; i++)  { 
      mAtt[mLPo[j]+i] |= PIVA_Select;
      mCPBuffer[k] = mText[mLPo[j]+i]; k++; 
      }
    if (j<mCPendL-1) mCPBuffer[k] = '\n'; k++;
    }
  mCPBuffer[l] = '\0';
  if (l1 > mCPbeginL) l1 = mCPbeginL;
  if (l2 < (mCPendL-1))  l2 = mCPendL-1;
  }
else {  
  l = 0;   
  mCPbeginC =  mCPendC = mCPbeginL = mCPendL = -1; 
  mCPBufLen = 0;
  mCPBuffer = new char[10];  
  mCPBuffer[0] = mCPBuffer[1] = '\0';
  }

//  printf("*DBG* Save_Display %d %d \n", l1, l2);

if (l2 > -1) DisplayLines(mWGrC, l1, l2);
}

/* --Methode-- */
void PIConsole::Process(PIMessage msg, PIMsgHandler* sender, void* data)
{
int off;
// printf("PIConsole::Process() UserMsg()= %d ModMsg= %d \n", (int)UserMsg(msg), 
//        (int)ModMsg(msg) );

if (sender == opmc) { // Menu des options 
  bool fgr = true;
  switch (UserMsg(msg)) {
    case 3101 :
      SelectFont(PI_SmallSizeFont);
      AutoSize();
      break;
    case 3102 :
      SelectFont(PI_NormalSizeFont);
      AutoSize();
      break;
    case 3103 :
      SelectFont(PI_LargeSizeFont);
      AutoSize();
      break;
    case 3201 :
      SelFgBgCol(PI_Black, PI_White);
      break;
    case 3202 :
      SelFgBgCol(PI_White, PI_Black);
      break;
    case 3203 :
      SelFgBgCol(PI_Yellow, PI_Black);
      break;
    case 3301 :
      AutoSize(25, 132);
      break;
    case 3302 :
      AutoSize(20, 80);
      break;
    case 3303 :
      AutoSize(10, 80);
      break;      
    case 3304 :
      AutoSize(5, 60);
      break;      
    default :
      fgr = false;
      break;
    }
  if (fgr) Refresh();
  delete opmc;  opmc = NULL;
  }
// Le traitement du scroll-bar
else if (((ModMsg(msg) == PIMsg_DataChanged) || 
	  (ModMsg(msg) == PIMsg_Drag) ) && (UserMsg(msg) == msgScb) ) {
  off = * ((int *)data);
//  printf(" PIConsole::Process() From ScrollBar : Off=%d \n ", off);
  SetDispOffset(off);
  Refresh();
  }
// Sinon, on retranmet le message
else ReSend(msg, sender, data); 
}

