// Module PI : Peida Interactive     PIBaseWdg
// Classe de base pour nouvelles composantes graphiques  96
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include <stdio.h>

#include "pibwdggen.h"
#include "pidrawer.h"
#include "pigraphps.h"
#include "pidrwtools.h"

static inline void dbl_swap(double& a, double& b) { double tmp = a; a = b; b = tmp;  } 

//++
// Class	PIBaseWdg
// Lib		PI
// include	pibwdggen.h
//
//   	Classe de base pour la création de nouvelles composantes graphiques.
//	Le traitement des événements liés à l'écran, clavier, souris se fait
//	à travers des méthodes de cette classe : "Resize()", "But1Press()", ...
//	La classe "PIBaseWdg" supporte en outre un mécanisme permettant
//	d'associer un ou plusieurs objets traceur (*Drawer*) capables 
//	de se dessiner dans la fenêtre associée à l'objet.
//	La classe "PIBaseWdgGen" définit l'interface et ne peut pas être instanciée 
//	directement (Méthodes virtuelles pures). Les objets instanciés sont de la 
//	classe "PIBaseWdg" et le fichier entête correspondant est "PIBWDG_H"
//	défini dans "pisysdep.h".
//--
//++
// Links	Parents
// PIWdg
//--
//++
// Links	Voir aussi
// PIGraphic
// PIDrawer
//--

// ---------- Les methodes de la classe  PIBWdgGen ------------

//++
// Titre	Constructeur
//--
//++
//  PIBaseWdgGen(PIContainerGen *par, char *nom, int sx=10, int sy=10, int px=0, int py=0)
//	Création d'un objet contenu dans l'objet *PIContainer* "par", de
//	taille "sx,sy", se trouvant à la position "px,py" avec le nom "nom"	
//--

//++
// Titre	Méthodes
//--
//++
// void  SetBackgroundColor(PIColors col=PI_White)
//	Choix de la couleur de fond.
// void  EraseWindow(int x0=0, int y0=0, int dx=0, int dy=0) 
//	Efface la zone rectangulaire définie par le coin "(x0,y0)" ,
//	de taille "dx,dy" à l'aide de la couleur de fond.
// void  SelPointerShape(PIPointer ptr=PI_ArrowPointer) 
//	Choix de la forme du pointeur (souris) pour la fenêtre 
//	associée à l'objet.
//|	PI_ArrowPointer, PI_CrossPointer, PI_HandPointer, PI_TDLRArrowPointer
// PIColors  GetBackgroundColor()
//	Renvoie la couleur de fond
// PIPointer  GetPointerShape() 
//	Renvoie la forme du pointeur
//--

//++
// Titre	Activation du traitement des événements
//--
//++
// void  ActivatePtrCross()
//	Active l'appel aux méthodes "Enter()" et "Leave()" lorsque la souris
//	rentre ou sort de la fenêtre correspondante à l'objet
// void  ActivateButton(int b)
//	Active l'appel aux méthodes "ButbPress()" et "ButxRelease()" pour
//	le bouton "b" de la souris ("b=1,2,3")
// void  ActivateMove(int b) 
//	Active l'appel aux méthodes "PtrbMove()" lors des mouvements de
//	la souris avec le bouton "b" enfoncé ("b=1,2,3")
//	L'appel avec "bid=0" active l'appel à la méthode "PtrMove()"
//	Lors des mouvements de souris sans bouton enfoncé.
// void  ActivateKeyboard()
//	Active l'appel à la méthode "Keyboard()" lors de l'appui sur les 
//	touches du clavier.
//--

 
/* --Methode-- */
PIBaseWdgGen::PIBaseWdgGen(PIContainerGen *par, char *nom,
                       int sx, int sy, int px, int py) :
PIWdg(par, nom, sx, sy, px, py)
{
mDrwId=0;   // Compteur des numeros sequentiel des PIDrawer
mAdfg=false;    // Pour controle d'appel de RemoveDrawer()
mWGrC = NULL;   // PIGraphic (Contexte graphique) associe a la fenetre}
                // Doit etre cree par PIBaseWdgX/Mac/... 
SetDefaultDrawRectangle(0., 0., 1., 1., true);
SetDefaultDrawerLimits(0., 1., 0., 1., kAxeDirLtoR, kAxeDirUpDown);
}

/* --Methode-- */
PIBaseWdgGen::~PIBaseWdgGen()
{
delete mWGrC;
DeleteDrawers();
if (PIDrwTools::GetCurrentBaseWdg() == this) 
  PIDrwTools::SetCurrentBaseWdg(NULL);
}
 

/* --Methode-- 
void PIBaseWdgGen::Refresh()
{
if ( IsVisible() )  { 
  Draw(mWGrC, 0, 0, XSize(), YSize());
  CallDrawers(mWGrC); 
}
return;
}
*/

//++
// Titre	Gestion des événements
//--
//++
// void  Draw(PIGraphic* g, int x0, int y0, int dx, int dy)
//	Méthode appelée lorsqu'il faut redessiner tout ou partie de
//	la fenêtre. La zone a rafraîchir est determinée par 
//	"(x0,y0)", "dx,dy" et "g" est l'objet "PIGraphic" 
//	qui doit être utilisé.
//	Cette méthode ne doit pas être appelé directement. 
//	Pour rafraîchir le contenu de la fenêtre, il faut appeler
//	la méthode "Refresh()"
// void  Resize()
//	Méthode appelée lorsque la taille de l'objet est modifiée.
//	"XSize()", "YSize()" fournissent la nouvelle taille de l'objet.
// void  Enter()
//	Méthode appelée lorsque le pointeur rentre dans la fenêtre
//	(Si "ActivatePtrCross()" a été appelé )
// void  Leave()
//	Méthode appelée lorsque le pointeur quitte la fenêtre
//	(Si "ActivatePtrCross()" a été appelé )
// void  ButbPress(int x, int y)  (b=1,2,3)
//	Méthode appelée lorsque le bouton "b=1,2,3" de la souris est enfoncé.
//	"x,y" : Position de la souris
//	(Si "ActivateButton(b)" a été appelé )
// void  ButbRelease(int x, int y)   (b=1,2,3)
//	Méthode appelée lorsque le bouton "b=1,2,3" de la souris est relaché.
//	"x,y" : Position de la souris
//	(Si "ActivateButton(b)" a été appelé )
// void  PtrMove(int x, int y)
//	Méthode appelée lors des mouvements de la souris. "x,y" : Position de la souris
//	(Si "ActivateMove(0)" a été appelé )
// void  PtrbMove(int x, int y)     (b=1,2,3)
//	Méthode appelée lors des mouvements de la souris, avec le 
//	bouton "b=1,2,3" appuyé. "x,y" : Position de la souris
//	(Si "ActivateMove(b)" a été appelé )
// void  Keyboard(int key, PIKeyModifier kmod)
//	Méthode appelée en réponse à des actions sur le clavier.
//	"key" est le code ASCII de la touche enfoncé - "kmod" indique
//	si des touches speciaux étaient enfoncés en même temps.
//|	kmod =  PIKM_Blank |  PIKM_Shift | PIKM_Alt | PIKM_Cntl
//	Des constantes ont été définis pour certaines touches particulières
//|	PIK_Return , PIK_Enter , PIK_BackSpace, PIK_Delete
//|	PIK_Tab , PIK_Escape , PIK_Up , PIK_Down, 
//|	PIK_Right , PIK_Left , PIK_Previous , PIK_Next
//--

/* --Methode-- */
void PIBaseWdgGen::Draw(PIGraphic* g, int x0, int y0, int dx, int dy)
{
#ifdef DEBUG_RZXB
puts("Debug_PIBaseWdgGen::Draw() Efface la fenetre par defaut ! ");
#endif
if (g->kind() != PI_PSFileGraphics) EraseWindow(x0, y0, dx, dy);
return;
}

/* --Methode-- */
void PIBaseWdgGen::Resize()
{
#ifdef DEBUG_RZXB
puts("Debug_PIBaseWdgGen::Resize()  resize notify ");
#endif
return;
}


/* --Methode-- */
void PIBaseWdgGen::Enter()
{
#ifdef DEBUG_RZXB
puts("Debug_PIBaseWdgGen::Enter() enter notify ");
#endif
return;
}

/* --Methode-- */
void PIBaseWdgGen::Leave()
{
#ifdef DEBUG_RZXB
puts("Debug_PIBaseWdgGen::Leave() leave notify ");
#endif
return;
}


/* --Methode-- */
void PIBaseWdgGen::But1Press(int /*x*/, int /*y*/)
{
Send(Msg(), PIMsg_Active);
#ifdef DEBUG_RZXB
printf ("PIBaseWdgGen::But1Press PosX,Y= %d %d \n", x, y);
#endif
return;
}

/* --Methode-- */
void PIBaseWdgGen::But1Release(int /*x*/, int /*y*/)
{
#ifdef DEBUG_RZXB
printf ("PIBaseWdgGen::ButReleasePosX,Y= %d %d \n", x, y);
#endif
return;
}

/* --Methode-- */
void PIBaseWdgGen::But2Press(int /*x*/, int /*y*/)
{
return;
}

/* --Methode-- */
void PIBaseWdgGen::But2Release(int /*x*/, int /*y*/)
{
return;
}

/* --Methode-- */
void PIBaseWdgGen::But3Press(int /*x*/, int /*y*/)
{
return;
}

/* --Methode-- */
void PIBaseWdgGen::But3Release(int /*x*/, int /*y*/)
{
return;
}

/* --Methode-- */
void PIBaseWdgGen::PtrMove(int /*x*/, int /*y*/)
{
#ifdef DEBUG_RZXB
printf ("Debug_PIBaseWdgGen::PtrMove PosX,Y= %d %d \n", x, y);
#endif
return;
}

/* --Methode-- */
void PIBaseWdgGen::Ptr1Move(int /*x*/, int /*y*/)
{
#ifdef DEBUG_RZXB
printf ("Debug_PIBaseWdgGen::Ptr1Move PosX,Y= %d %d \n", x, y);
#endif
return;
}

/* --Methode-- */
void PIBaseWdgGen::Ptr2Move(int /*x*/, int /*y*/)
{
return;
}


/* --Methode-- */
void PIBaseWdgGen::Ptr3Move(int /*x*/, int /*y*/)
{
return;
}

/* --Methode-- */
void PIBaseWdgGen::Keyboard(int /*key*/, PIKeyModifier /*kmod*/)
{
#ifdef DEBUG_RZXB
printf("PIBaseWdgGen::Keyboard: Key= %d (%c)  Mod= %d \n", key, (char)key, (int)kmod); 
#endif
return;
}


/* --Methode-- */
void PIBaseWdgGen::PSPrint(PSFile * psf, int ofx, int ofy)
{
if (!psf) return;
PIGraphicPS grpsf(psf, (PIWdg *)this, ofx, ofy);
Draw(&grpsf, 0, 0, XSize(), YSize());
CallDrawers(&grpsf); 
return;
}

//++
// Titre	Gestion des traceurs (PIDrawer)
//	Un même "PIDrawer" peut être attaché simultanement à plusieurs "PIBaseWdg", ainsi
//	que plusieurs fois au même "PIBaseWdg", avec des identificateurs différents.
//--
//++
// void  SetDefaultDrawRectangle(PIGrCoord x1, PIGrCoord y1, PIGrCoord x2, PIGrCoord y2, bool psz=true)
//	Définition du rectangle d'attachement par défaut. "(x1,y1)" et "(x2,y2)" determinent
//	les positions des deux coins opposés.
//	si "psz=true", les coordonnées sont supposées être des facteurs multiplicatifs 
//	des dimensions de l'objet.
// void  SetDefaultDrawerLimits(double xmin, double xmax, double ymin, double ymax, -
//                              int axrl=kAxeDirSame, int ayud=kAxeDirSame)
//	Définition du système de coordonnées par défaut d'un drawer attaché en "autolim=true"
//
//--

/* --Methode-- */
void PIBaseWdgGen::SetDefaultDrawRectangle(PIGrCoord x1, PIGrCoord y1, PIGrCoord x2, PIGrCoord y2, bool psz)
{
mDrX1 = x1;  mDrY1 = y1;
mDrX2 = x2;  mDrY2 = y2;   mDrElastic = psz;
}

/* --Methode-- */
void PIBaseWdgGen::SetDefaultDrawerLimits(double xmin, double xmax, double ymin, double ymax,
                                          int axrl, int ayud)
{
mDrXmin = xmin;   mDrXmax = xmax; 
mDrYmin = ymin;   mDrYmax = ymax; 
mDXdir = axrl;    mDYdir = ayud;
}


//++
// int  AddDrawer(PIDrawer* drw, PIGrCoord x1, PIGrCoord y1, PIGrCoord x2, PIGrCoord y2, -
//                bool psz=true, bool clip=true, bool ad=false)
//	Attachement d'un "PIDrawer" au rectangle défini les coordonnées des deux coins opposés.
//	si "psz=true", les coordonnées sont supposées être des facteurs multiplicatifs 
//	des dimensions de l'objet.
//	si "clip=true", "PIDrawer* drw" ne peut dessiner en dehors de son rectangle
//	d'attachement.
//	si "ad=true", le "PIDrawer* drw" est détruit lors de la destruction de l'objet PIBaseWdg.
//	La méthode retourne l'identificateur du drawer attaché.
// int  AddDrawer(PIDrawer* drw, bool autolim=false, bool clip=true, bool ad=false)
//	Ajout d'un "PIDrawer" au rectangle d'attachement par défaut. 
//	Si "autolim=true", l'objet "PIBaseWdg" impose l'espace de coordonnées utilisateur
//	à l'aide des valeurs définies par "SetDefaultDrawerLimits()".
//	Si ""clip=true", "PIDrawer* drw" ne peut dessiner en dehors de son rectangle 
//	d'attachement.
//	si "ad=true", le "PIDrawer* drw" est détruit lors de la destruction de l'objet PIBaseWdg.
//	La méthode retourne l'identificateur du drawer attaché.
//--

/* --Methode-- */
int PIBaseWdgGen::AddDrawer(PIDrawer* drw, PIGrCoord x1, PIGrCoord y1,  
                            PIGrCoord x2, PIGrCoord y2, bool psz, bool clip, bool ad)
{
if (drw == NULL)   return(0);
mDrwId++;
BWDrwId did;
did.x1 = x1;  did.y1 = y1;
did.x2 = x2;  did.y2 = y2;
did.autoszp = did.autolim = false;   
did.elastic = psz;  did.clip = clip;
did.id = mDrwId;  did.ad = ad; 
did.drw = drw;
mDrwList.push_back(did);
drw->Attach(this, mDrwId);
return(mDrwId);
}

/* --Methode-- */
int PIBaseWdgGen::AddDrawer(PIDrawer* drw, bool autolim, bool clip, bool ad)
{
if (drw == NULL)   return(0);
mDrwId++;
BWDrwId did;
did.x1 = 0.;  did.y1 = 0.;
did.x2 = 1.;  did.y2 = 1.;
did.autoszp = true;   did.autolim = autolim;   
did.elastic = false;   did.clip = clip;
did.id = mDrwId;  did.ad = ad; 
did.drw = drw;
mDrwList.push_back(did);
drw->Attach(this, mDrwId);
return(mDrwId);
}


//++
// void  MoveResizeDrawer(int id, PIGrCoord x1, PIGrCoord y1, PIGrCoord x2, PIGrCoord y2, bool psz=true)
//	Modification du rectangle d'attachement du PIDrawer avec l'identificateur "id" , retourné
//	par "AddDrawer()"
// void  RemoveDrawer(int id)
//	Ote le drawer "id" de la liste
// void  DeleteDrawers()
//	Ote tous les drawers de la liste. Détruit ceux ajoutés avec l'option "ad=true" 
//--

/* --Methode-- */
void PIBaseWdgGen::MoveResizeDrawer(int id,  PIGrCoord x1, PIGrCoord y1,  PIGrCoord x2, PIGrCoord y2, bool psz)
{
vector<BWDrwId>::iterator it;
for(it = mDrwList.begin(); it != mDrwList.end(); it++)  
  if ((*it).id == id) { 
    (*it).x1 = x1;  (*it).y1 = y1;
    (*it).x2 = x2;  (*it).y2 = y2;
    (*it).elastic = psz;
    break;
  }
return;
}

/* --Methode-- */
void PIBaseWdgGen::RemoveDrawer(int id)
{
if (mAdfg)  return;
vector<BWDrwId>::iterator it;
for(it = mDrwList.begin(); it != mDrwList.end(); it++)
  if ((*it).id == id) 
    { (*it).drw->Detach(this, id); mDrwList.erase(it); break; }
return;
}

/* --Methode-- */
void PIBaseWdgGen::DeleteDrawers()
{
mAdfg = true;
vector<BWDrwId>::iterator it;
for(it = mDrwList.begin(); it != mDrwList.end(); it++)  
  if ((*it).ad)  delete (*it).drw;
mDrwList.erase(mDrwList.begin(), mDrwList.end() );
mAdfg = false;
return;
}

//++
// int  NbDrawers()
//	Retourne le nombre de PIDrawer attachés
// PIDrawer*  GetDrawerId(int id)
//	Retourne le PIDrawer avec l'identificateur id
// PIDrawer*  GetDrawer(int n)
//	Retourne le PIDrawer "n=0..NbDrawers()-1" de la liste
//--

/* --Methode-- */
int PIBaseWdgGen::NbDrawers()
{
return(mDrwList.size());
}


/* --Methode-- */
PIDrawer* PIBaseWdgGen::GetDrawer(int n)
{
if ( (n < 0) || (n >= mDrwList.size()) ) return(NULL);
return(mDrwList[n].drw);
}

/* --Methode-- */
PIDrawer* PIBaseWdgGen::GetDrawerId(int id)
{
vector<BWDrwId>::iterator it;
for(it = mDrwList.begin(); it != mDrwList.end(); it++)  
  if ((*it).id == id)  return((*it).drw);
return(NULL);
}


//++
// void  CallDrawers(PIGraphic* g, int x0=0, int y0=0, int dx=0, int dy=0)
//	Appel de la méthode de tracé pour tous les Drawers
// void  CallDrawer(int id);
//	Appel de la méthode de tracé pour le drawer avec l'identificateur "id"
//--

/* --Methode-- */
void PIBaseWdgGen::CallDrawers(PIGraphic* g, int x0, int y0, int dx, int dy)
{
double xmin,ymin, xmax, ymax;
PIGraphicUC* guc;
int drx0, dry0, drdx, drdy;
int ddrx0, ddry0, ddrdx, ddrdy;

if (mDrElastic) { 
  ddrx0 = (int)((double) mDrX1 * (double)XSize());
  ddry0 = (int)((double) mDrY1 * (double)YSize());
  ddrdx = (int)((double) mDrX2 * (double)XSize()) - ddrx0 ;
  ddrdy = (int)((double) mDrY2 * (double)YSize()) - ddry0 ;
  }
else {
  ddrx0 = (int)mDrX1;   ddry0 = (int)mDrY1;  
  ddrdx = (int)mDrX2-ddrx0;   ddrdy = (int)mDrY2-ddry0;  
  }

bool sxy = false;
if ((dx > 0) && (dy > 0))  sxy = true;

vector<BWDrwId>::iterator it;
bool fgc=false;
for(it = mDrwList.begin(); it != mDrwList.end(); it++) {
  g->SaveGraphicAtt();
  if ( (*it).autoszp )   // Taille/position  par defaut impose par PIBaseWdg 
    { drx0 = ddrx0;  dry0 = ddry0;   drdx = ddrdx;  drdy = ddrdy; } 
  else {
    if ( (*it).elastic  ) {   // Taille/position defin en fraction de la taille du PIBaseWdg
      drx0 = (int)((double)(*it).x1 * (double)XSize());
      dry0 = (int)((double)(*it).y1 * (double)YSize());
      drdx = (int)((double)(*it).x2 * (double)XSize()) - drx0 ;
      drdy = (int)((double)(*it).y2 * (double)YSize()) - dry0 ;
      }
    else {
      drx0 = (int)(*it).x1 ;  dry0 = (int)(*it).y1 ; 
      drdx = (int)(*it).x2-drx0 ;  drdy = (int)(*it).y2-dry0 ; 
      }
    }
// L'ordre des operations ici est importante 
  if ( (*it).autolim )  
     (*it).drw->SetLimits(mDrXmin, mDrXmax, mDrYmin, mDrYmax, mDXdir, mDYdir);     
  guc = (*it).drw->SetDrwWdg(this, drx0, dry0, drdx, drdy, g);
  if ((*it).clip)  { g->SetClipRectangle(drx0, dry0, drdx , drdy);  fgc = true; }
  (*it).drw->SelGraAtt(guc);
  if (sxy) {
    guc->GrC2UC(x0, y0, xmin, ymin);
    guc->GrC2UC(x0+dx, y0+dy, xmax, ymax);
    }
  else {
    guc->GrC2UC(0, 0, xmin, ymin);
    guc->GrC2UC(XSize(), YSize(), xmax, ymax);    
    } 
  if (xmin > xmax)  dbl_swap(xmin,xmax);
  if (ymin > ymax)  dbl_swap(ymin,ymax);
  (*it).drw->Draw(guc, xmin, ymin, xmax, ymax);
  g->RestoreGraphicAtt();
  }
if (fgc)  g->ClearClipRectangle();

return;
}

/* --Methode-- */
void PIBaseWdgGen::CallDrawer(int id)
{
double xmin,ymin, xmax, ymax;
PIGraphicUC* guc;
int drx0, dry0, drdx, drdy;
int ddrx0, ddry0, ddrdx, ddrdy;

PIGraphic* g = WindowGraphic();

if (mDrElastic) { 
  ddrx0 = (int)((double) mDrX1 * (double)XSize());
  ddry0 = (int)((double) mDrY1 * (double)YSize());
  ddrdx = (int)((double) mDrX2 * (double)XSize()) - ddrx0 ;
  ddrdy = (int)((double) mDrY2 * (double)YSize()) - ddry0 ;
  }
else {
  ddrx0 = (int)mDrX1;   ddry0 = (int)mDrY1;  
  ddrdx = (int)mDrX2-ddrx0;   ddrdy = (int)mDrY2-ddry0;  
  }
vector<BWDrwId>::iterator it;
for(it = mDrwList.begin(); it != mDrwList.end(); it++) 
  if ((*it).id == id) {
    g->SaveGraphicAtt();
    if ( (*it).autoszp )   // Taille/position  par defaut impose par PIBaseWdg 
      { drx0 = ddrx0;  dry0 = ddry0;   drdx = ddrdx;  drdy = ddrdy; } 
    else {
      if ( (*it).elastic  ) {   // Taille/position defin en fraction de la taille du PIBaseWdg
        drx0 = (int)((double)(*it).x1 * (double)XSize());
        dry0 = (int)((double)(*it).y1 * (double)YSize());
        drdx = (int)((double)(*it).x2 * (double)XSize()) - drx0 ;
        drdy = (int)((double)(*it).y2 * (double)YSize()) - dry0 ;
        }
      else {
        drx0 = (int)(*it).x1 ;  dry0 = (int)(*it).y1 ; 
        drdx = (int)(*it).x2-drx0 ;  drdy = (int)(*it).y2-dry0 ; 
        }
      }
// L'ordre des operations ici est importante 
    if ( (*it).autolim ) 
       (*it).drw->SetLimits(mDrXmin, mDrXmax, mDrYmin, mDrYmax, mDXdir, mDYdir);
    guc = (*it).drw->SetDrwWdg(this, drx0, dry0, drdx, drdy, g);
    if ((*it).clip)  g->SetClipRectangle(drx0, dry0, drdx , drdy);
    (*it).drw->SelGraAtt(guc);
    guc->GrC2UC(0, 0, xmin, ymin);
    guc->GrC2UC(XSize(), YSize(), xmax, ymax);     
    if (xmin > xmax)  dbl_swap(xmin,xmax);
    if (ymin > ymax)  dbl_swap(ymin,ymax);
    (*it).drw->Draw(guc, xmin, ymin, xmax, ymax);
    g->RestoreGraphicAtt();    
    if ((*it).clip)  g->ClearClipRectangle();
    break;
  }
return;
}


/* --Methode-- */
PIGraphic* PIBaseWdgGen::WindowGraphic()
{
return(mWGrC);
}
