#include<stdio.h>
#include<iostream>

#include "pisysdep.h"
#include PIMENUBAR_H

#include <Xm/Xm.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/CascadeB.h>
#include <Xm/Separator.h>

/************* PIMenuX ************/

// Quand on cree les menus comme fils du Top-Widget, ca rentre en 
// conflit avec certaains Window-Manager ...
// La fonction suivante permet de bypasser ce probleme- Le createur d'application
// X doit donc appeler la fonction ci-dessous pour initialiser menuXTopWdg
//                                               Reza 4/11/97
static SysDWdg menuXTopWdg = NULL;
void SetTopWdgForMenuX(SysDWdg mtw) {
  menuXTopWdg = mtw;
}

// Pour ces sal... de XmString  (ds piwdgx.cc)
int CStrFrXmStr(XmString xms, char* buff, int nmx);


// Fonction prive (CallBack)
static void menu_action(Widget w, XtPointer usd, XtPointer calld);
static void menu_toggle_action(Widget w, XtPointer usd, XmToggleButtonCallbackStruct* calld);

/* Nouvelle-Fonction */
void  menu_action(Widget /*w*/, XtPointer usd, XtPointer /*calld*/)
{
PIMenuX *pim;
_pimxit_ *cusd;

cusd = ( _pimxit_ *)usd ;
pim = (PIMenuX *) (cusd->pmen);
// pim->Hide();
pim->SendSelf(cusd->msg, PIMsg_Click );
#ifdef DEBUG_PIMenuX
printf("%%%Debug_menu_action: Msg= %d  %lx\n", (int)cusd->msg, (long)pim);
#endif
return;
}

// Variable globale pour Toggle Item's  Send(..., ..., data); 
static bool toggmenbut = false;

/* Nouvelle-Fonction */
void  menu_toggle_action(Widget /*w*/, XtPointer usd, XmToggleButtonCallbackStruct* calld)
{
PIMenuX *pim;
_pimxit_ *cusd;

cusd = ( _pimxit_ *)usd ;
pim = (PIMenuX *) (cusd->pmen);
if (calld->set == True)  cusd->togstate = toggmenbut = true;
else  cusd->togstate = toggmenbut = false;
pim->SendSelf(cusd->msg, PIMsg_DataChanged, (void *)(&toggmenbut));
return;
}



#define NBITEMB  10

/* --Methode-- */
PIMenuX::PIMenuX(PIWdg* par, const char *nom, PIMenuType pdpu) 
: PIMenuGen(par, nom, pdpu)
{
mNItem = 0;  mNItemMax = NBITEMB; 
mNSep = 0;
mBut = new _pimxit_[mNItemMax];

SysDWdg tw;
if (par == NULL) { 
  tw = menuXTopWdg;
  if (tw == NULL) tw = PIXtTopWdg(); 
}
else  tw = par->XtWdg();
// Si le type de menu pdpu n'est pas specifie, on le decide 
// en fonction du type de l'objet parent 
if (pdpu == k_UTMenu) {
  if  ( (par->kind() == PIMenu::ClassId) || (par->kind() == PIMenubar::ClassId) )  pdpu = k_PulldownMenu ;
  else pdpu = k_PopupMenu ;
}
if (pdpu == k_PopupMenu)
  XtWdg() = XmCreatePopupMenu( tw, (char *)nom, NULL, 0);   // const_cast
else 
  XtWdg() = XmCreatePulldownMenu( tw, (char *)nom, NULL, 0); // const_cast

mMTyp = pdpu;
wmis = k_wmi_normal ;
stmng = 0;
}

/* --Methode-- */
PIMenuX::~PIMenuX()
{
// for(int i=0; i<mNItem; i++)
//  XtDestroyWidget(mBut[i].itb); Le destroy est fait par le widget menu lui-meme
delete[] mBut ;
}


/* --Methode-- */
void PIMenuX::AppendItem(const char* nom, PIMessage msg, char* sc)
{
AddItem(nom, msg, 0, sc, NULL);
}

/* --Methode-- */
void PIMenuX::AppendCheckItem(const char* nom, PIMessage msg, char* sc)
{
AddItem(nom, msg, 1, sc, NULL);
}

/* --Methode-- */
void PIMenuX::AppendPDMenu(PIMenuGen *pdm, char* sc)
{
if (pdm->MType() !=  k_PulldownMenu) {
  cerr << "PIMenuX::AppendPDMenu()/Erreur: PullDownMenu seulement ... " << endl;
  return;
  }
AddItem(NULL, pdm->Msg(), 2, sc, (PIMenuX*)pdm);
}

/* --Methode-- */
void PIMenuX::AppendSeparator()
{
  Arg warg[3];
  int na = 0;
  XtSetArg(warg[na], XmNorientation, XmHORIZONTAL); na++;
  XtSetArg(warg[na], XmNseparatorType, XmSHADOW_ETCHED_IN); na++;

  char buff[64];
  sprintf(buff, "%s_sep_%d", this->Nom().substr(0, 32).c_str(), mNSep); 
  Widget sep = XmCreateSeparator(this->XtWdg(), buff, warg, na);
  XtManageChild(sep);
  mNSep++;
  //  cout << " DBG PIMenuX::AppendSeparator() : " << buff << endl;
}

/* --Methode-- */
void PIMenuX::DeleteItem(const char *nom)
{
DeleteItemNum(GetNumItem(nom));
}

/* --Methode-- */
void PIMenuX::DeleteItemMsg(PIMessage msg)
{
DeleteItemNum(GetNumItemMsg(msg));
}

/* --Methode-- */
void PIMenuX::SetSensitivity(const char *nom, bool sens)
{
SetSensitivityNum(GetNumItem(nom), sens);
}

/* --Methode-- */
void PIMenuX::SetSensitivityMsg(PIMessage msg, bool sens)
{
SetSensitivityNum(GetNumItemMsg(msg), sens);
}

/* --Methode-- */
bool PIMenuX::IsSensitive(const char *nom)
{
return(IsSensitiveNum(GetNumItem(nom)) );
}

/* --Methode-- */
bool PIMenuX::IsSensitiveMsg(PIMessage msg)
{
return(IsSensitiveNum(GetNumItemMsg(msg)) ); 
}

/* --Methode-- */
bool PIMenuX::IsSensitiveNum(int n)
{
if ((n < 0) || (n >= mNItem))  return(false);
if (XtIsSensitive(mBut[n].itb) == TRUE)  return(true);
else return(false);
}

/* --Methode-- */
void PIMenuX::SetSensitivityNum(int n, bool sens)
{
if ((n < 0) || (n >= mNItem))  return;
if (sens) XtSetSensitive(mBut[n].itb, TRUE);
else XtSetSensitive(mBut[n].itb, FALSE);
}

/* --Methode-- */
void PIMenuX::DeleteItemNum(int n)
{
if ((n < 0) || (n >= mNItem))  return;
XtDestroyWidget(mBut[n].itb);
for(int i=n; i<mNItem-1; i++)
  {
  String cbckn;
  XtCallbackProc cbproc;
  if (mBut[i+1].toggpdm < 2) {
    if (mBut[i+1].toggpdm == 1)    // ToggleItem 
      { cbckn = XmNvalueChangedCallback;  cbproc = (XtCallbackProc)menu_toggle_action; }
    else { cbckn = XmNactivateCallback;  cbproc = menu_action; }

    XtRemoveCallback(mBut[i+1].itb, cbckn, cbproc, (XtPointer)(mBut+i+1));
    mBut[i] = mBut[i+1];
    XtAddCallback(mBut[i].itb, cbckn, cbproc, (XtPointer)(mBut+i));
    }
  else mBut[i] = mBut[i+1]; 
  }
mNItem--;
}


/* --Methode-- */
void PIMenuX::SetState(const char *nom, bool st)
{
SetStateNum(GetNumItem(nom), st);
}

/* --Methode-- */
void PIMenuX::SetStateMsg(PIMessage msg, bool st)
{
SetStateNum(GetNumItemMsg(msg), st);
}

/* --Methode-- */
void PIMenuX::SetStateNum(int n, bool st)
{
if ((n < 0) || (n >= mNItem))  return;
if (mBut[n].toggpdm != 1 ) return;  // Pas un toggle-item
Arg warg[2];
if (st) XtSetArg(warg[0],XmNset, True);
else XtSetArg(warg[0],XmNset, False);
XtSetValues(mBut[n].itb, warg, 1);
mBut[n].togstate = st;
return;
}

/* --Methode-- */
bool PIMenuX::GetState(const char *nom)
{
return( GetStateNum(GetNumItem(nom)) );
}

/* --Methode-- */
bool PIMenuX::GetStateMsg(PIMessage msg)
{
return( GetStateNum(GetNumItemMsg(msg)) );
}

/* --Methode-- */
bool PIMenuX::GetStateNum(int n) 
{
if ((n < 0) || (n >= mNItem))  return(false);
if (mBut[n].toggpdm != 1 ) return(false); 
return(mBut[n].togstate); 
}




/* --Methode-- */
char* PIMenuX::ItemName(int n)
{
if ( (n < 0) || (n >= mNItem))  { mSItem[0] = '\0'; return(mSItem); }
else return (ItemNameWdg(mBut[n].itb));
}


/* --Methode-- */
char* PIMenuX::ItemNameWdg(SysDWdg itw)
{
XmString xmstr;
Arg warg[2];

XtSetArg(warg[0], XmNlabelString, &xmstr);
XtGetValues(itw, warg, 1);
if (xmstr)
  {
  CStrFrXmStr(xmstr, mSItem, 96);
  XmStringFree(xmstr);
  }
else mSItem[0] = '\0';

return(mSItem);
}


/* --Methode-- */
PIMessage PIMenuX::ItemMsg(int n)
{
if ( (n < 0) || (n >= mNItem))  return(0);
else return(mBut[n].msg);
}



/* --Methode-- */
int PIMenuX::GetNumItem(const char *nom)
{

for(int i=0; i<mNItem; i++)
  if (strcmp(ItemName(i),nom) == 0)  return(i);
return(-1);
}

/* --Methode-- */
int PIMenuX::GetNumItemMsg(PIMessage msg)
{
for(int i=0; i<mNItem; i++)
  if (mBut[i].msg == msg)   return(i);  
return(-1);
}

/* --Methode-- */
SysDWdg PIMenuX::GetItemWdg(int n)
{
if ((n<0) || (n>=mNItem))  return(NULL);
else return(mBut[n].itb);
}


/* --Methode-- */
void PIMenuX::AddItem(const char* nom, PIMessage msg, int tog, char* sc, PIMenuX *pdm)
{
_pimxit_ *buto;
SysDWdgClass wdgcl;
String cbckn;
XtCallbackProc cbproc;
Arg  warg[2];
int n=0;


if (mNItem >= mNItemMax)  {
  buto = mBut;
  mNItemMax += NBITEMB;
  mBut = new _pimxit_[mNItemMax];
  for(int i=0; i<mNItem; i++)    {
    if (buto[i].toggpdm < 2) {  // Pas de Cascade Menu
      if (buto[i].toggpdm == 1)  // ToggleItem
        { cbckn = XmNvalueChangedCallback;  cbproc = (XtCallbackProc)menu_toggle_action; }
      else { cbckn = XmNactivateCallback;  cbproc = menu_action; }
      XtRemoveCallback(buto[i].itb, cbckn, cbproc, (XtPointer)(buto+i));
      mBut[i] = buto[i];
      XtAddCallback(mBut[i].itb, cbckn, cbproc, (XtPointer)(mBut+i));
      }
    else mBut[i] = buto[i]; 
    }
  delete[] buto;
  }

if (tog < 2) {  // Pas de Cascade Menu
  if (tog == 1) {   // Toggle-Item 
    cbckn = XmNvalueChangedCallback;  cbproc = (XtCallbackProc)menu_toggle_action;
    wdgcl = xmToggleButtonWidgetClass;  
    XtSetArg(warg[0],XmNset, False); n = 1; }
  else {
    cbckn = XmNactivateCallback;  cbproc = menu_action;
    wdgcl = xmPushButtonWidgetClass;  n = 0; }

  mBut[mNItem].itb = XtCreateManagedWidget(nom, wdgcl, this->XtWdg(), warg, n) ; 
  XtAddCallback(mBut[mNItem].itb, cbckn, cbproc, (XtPointer)(mBut+mNItem));
  }
else {    // Creation Cascade-Menu
  XtSetArg(warg[0], XmNsubMenuId, pdm->XtWdg());
  mBut[mNItem].itb = XtCreateManagedWidget(pdm->Nom().c_str(), xmCascadeButtonWidgetClass, 
                                           XtWdg(), warg, 1) ;
  pdm->SetMsgParent(this);
  }

if (sc)  // Raccourci clavier 
  {
  char acc[16];
  XmString xmstr;
  n=0;
  sprintf(acc,"Ctrl/%c", sc[0]);
  xmstr = XmStringLtoRCreate(acc, XmSTRING_DEFAULT_CHARSET);
  XtSetArg(warg[n], XmNacceleratorText, xmstr); n++;
  sprintf(acc,"Ctrl<Key>%c", sc[0]);
  XtSetArg(warg[n], XmNaccelerator, acc); n++;
  XtSetValues(mBut[mNItem].itb, warg, n);  
  XmStringFree(xmstr);
  }

#ifdef DEBUG_PIMenuX
printf("DBG>> %d (%d) %lx \n", mNItem, mNItemMax, (long)(mBut+mNItem));
#endif
mBut[mNItem].pmen = this;
mBut[mNItem].msg = msg;
mBut[mNItem].toggpdm = tog;
mBut[mNItem].togstate = false;
mNItem++;
return;
}



/* --Methode-- 
void PIMenuX::Check()
{
printf("#Debug_CHECK: %d %d %d %d \n",XSize(), YSize(), XPos(), YPos() );  
for(int i=0; i<mNItem; i++)
  printf("##Debug_CHECK: %d %d %ld %lx %lx %lx \n", i, (int)mBut[i].toggpdm, 
         (long) mBut[i].msg, (long) mBut[i].itb, 
         (long) mBut[i].pmen, long (mBut+i));
return;
}
*/




void PIMenuX::Show()
{
if (MType() !=  k_PopupMenu) {
  cerr << "PIMenuX::Show()/Erreur: PopupMenu seulement ... " << endl;
  return;
  }

// XEvent  mevt;
XButtonEvent evt;

// XPeekEvent(PIXDisplay(), &mevt);
// evt = mevt.xbutton; 
evt.type = ButtonPress;
evt.display = PIXDisplay();
evt.window = XtWindow(PIXtTopWdg());
evt.root = DefaultRootWindow(evt.display);
evt.serial = PIXGetLastEventSerial(); 
evt.time = PIXGetLastEventTime();
evt.send_event = False;
evt.same_screen = XQueryPointer(evt.display, evt.window, &(evt.root), 
                    &(evt.subwindow), &(evt.x_root), &(evt.y_root), 
                    &(evt.x), &(evt.y), &(evt.state));
 
#ifdef DEBUG_PIMenuX
printf("XXDbg_Show : %d %d %d - %d %d \n", (int)evt.same_screen, 
       (int)evt.x_root, (int)evt.y_root, (int)evt.x, (int)evt.y);
#endif

if (evt.same_screen) XmMenuPosition(XtWdg(), &evt);
Manage();     
}

/* --Methode-- */
void PIMenuX::Show(PIWdg *w, int px, int py)
{
if (MType() !=  k_PopupMenu) {
  cerr << "PIMenuX::Show(w, px, py)/Erreur: PopupMenu seulement ... " << endl;
  return;
  }
// This method should only be used by a button press ...
int xr, yr;
Window cw, sw, rw;
SysDWdg xtw;
Display *dsp;
XButtonEvent evt;

xtw =  w->XtWdg();
dsp = XtDisplay(xtw);
sw = XtWindow(xtw);
rw = DefaultRootWindow(dsp);
XTranslateCoordinates(dsp, sw, rw, px, py, &xr, &yr, &cw);

evt.type = ButtonPress;
evt.display = dsp;
evt.window = sw;
evt.root = rw;
evt.x_root = xr;
evt.y_root = yr; 
evt.x = px;
evt.y = py;
evt.serial = PIXGetLastEventSerial(); 
evt.time = PIXGetLastEventTime();
evt.send_event = False;
evt.same_screen = True;
XmMenuPosition(XtWdg(), &evt);
// SetPos(xr, yr);
Manage();
return;    
}




