/*                       
	This file is part of the GVars3 Library.

	Copyright (C) 2005 The Authors

	This library is free software, see LICENSE file for details
*/
#include "gvars3/instances.h"
#include "gvars3/GUI_Motif.h"
#include "gvars3/GStringUtil.h"
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/FileSB.h>
#include <Xm/Label.h>
#include <Xm/ScrollBar.h>
#include <Xm/ToggleB.h>
#include <Xm/TextF.h>
#include <Xm/Protocols.h>
#include <Xm/AtomMgr.h>
#include <pthread.h>
#include <unistd.h>
#include <sstream>

using namespace std;
namespace GVars3
{

GUI_Motif::GUI_Motif(class GUI *pGUI, GVars2 *pGV2)
{
	mpGUI=pGUI;
	mpGV2=pGV2;
	mpDisplay=NULL;
	mpGUI->RegisterCommand("GUI.InitXInterface", InitXInterfaceCB, this);
	mpGUI->RegisterCommand("GUI_Motif.InitXInterface", InitXInterfaceCB, this);
}


void GUI_Motif::InitXInterfaceCB(void* ptr, string , string sParams)
{
  ((GUI_Motif*)(ptr))->InitXInterface(sParams);
}

void GUI_Motif::AddWindowCB(void* ptr, string , string sParams)
{
  ((GUI_Motif*)(ptr))->AddWindow(sParams);
}



void GUI_Motif::AddWindow(string sParams)
{
  if(!mpDisplay)
    {
      cout << "! GUI_Motif::AddWindow: No display. Call InitXInterface first." << endl;
      return;
    }
  
  vector<string> vs = ChopAndUnquoteString(sParams);
  if(((vs.size()<1) || (vs.size()>3)) )
    {
      cout << "! GUI_Motif::AddWindow: need 1 - 3 params: Name, Caption=Name, Width=200 " << endl;
      return;
    };
  
  if(mmWindows.count(vs[0])>0)
  {
    cout << "? GUI_Motif::AddWindow: A window with id \"" << vs[0] << "\" already exists." << endl;
    return;
  }
  
  string sCaption;
  if(vs.size()>=2)
    sCaption=vs[1];
  else
    sCaption=vs[0];

  
  GUIWindowStruct gws;
  gws.PushButtonMap.clear();
  gws.MonitorMap.clear();
  gws.ToggleButtonMap.clear();
  gws.SliderMap.clear();
  gws.nWidth = 200;
  if(vs.size()>2)
    {
      int *pn = ParseAndAllocate<int>(vs[2]);
      if(pn)
	{
	  gws.nWidth=*pn;
	  delete pn;
	};      
    }
  gws.nHeight = 30;
  gws.die=0;
  
  pthread_mutex_lock((pthread_mutex_t*)mpMutex);
  
  Arg al[10];
  int ac = 0;
  gws.wTopLevel=XtAppCreateShell(sCaption.c_str(), sCaption.c_str(), applicationShellWidgetClass, mpDisplay, al, ac);

  ac=0;
  XtSetArg(al[ac], XmNpacking, XmPACK_TIGHT);ac++;
  XtSetArg(al[ac], XmNorientation, XmVERTICAL);ac++;
  XtSetArg(al[ac], XmNadjustLast, False);ac++;
  XtSetArg(al[ac], XmNresizeHeight, True); ac++;
  XtSetArg(al[ac], XmNresizeWidth, False); ac++;
  
  gws.wRowCol=XmCreateRowColumn(gws.wTopLevel,const_cast<char*>("GUI_Motif_rowcol"),al,ac);
  
  XtManageChild(gws.wRowCol);
  XtRealizeWidget(gws.wTopLevel);
  
  XEvent xevent;
  XtAppNextEvent(mxtac,&xevent);
  XtDispatchEvent(&xevent);
  DoMotifEvents();
  XResizeWindow(mpDisplay, XtWindow(gws.wTopLevel), gws.nWidth, gws.nHeight);
  XResizeWindow(mpDisplay, XtWindow(gws.wRowCol), gws.nWidth, gws.nHeight);
  DoMotifEvents();
  
  
  Atom delwinAtom1;
  delwinAtom1 = XmInternAtom (mpDisplay,const_cast<char*>("WM_DELETE_WINDOW"), False);
  XmAddWMProtocolCallback (gws.wTopLevel, delwinAtom1, RemoveWindowCB, this);
  XtVaSetValues(gws.wTopLevel, XmNdeleteResponse, XmDO_NOTHING, NULL);
  
  mmWindows[vs[0]]=gws;

  mpGUI->RegisterCommand(vs[0] + ".AddPushButton", AddPushButtonCB, this);
  mpGUI->RegisterCommand(vs[0] + ".AddToggleButton", AddToggleButtonCB, this);
  mpGUI->RegisterCommand(vs[0] + ".AddMonitor", AddMonitorCB, this);
  mpGUI->RegisterCommand(vs[0] + ".AddLabel", AddLabelCB, this);
  mpGUI->RegisterCommand(vs[0] + ".AddSlider", AddSliderCB, this);
  mpGUI->RegisterCommand(vs[0] + ".AddSpin", AddSpinCB, this);
  mpGUI->RegisterCommand(vs[0] + ".Destroy", DestroyCB, this);
  
  pthread_mutex_unlock((pthread_mutex_t*)mpMutex);

  
};


void GUI_Motif::InitXInterface(string sParams)
{
	vector<string> vs = ChopAndUnquoteString(sParams);
	string sDisplay="";
	if(vs.size()>0)
		sDisplay=vs[0];
	if(vs.size()>1)
		msName = vs[1];
	else
		msName = "GUI";


	if(mpDisplay)
	{
		cout << "??GUI_Motif::InitXInterface: display already initialised.." << endl;
		return;
	}

	if(sDisplay=="")
		mpDisplay=XOpenDisplay(NULL);
	else
		mpDisplay=XOpenDisplay(sDisplay.c_str());

	if(!mpDisplay)
	{
		cout << "??GUI_Motif::InitXInterface: Could not open display \""<<sDisplay<< "\"." << endl;
		return;
	}


	// Ok, set up some widgets!
	int argc=1;
	char* argv[] = {0};

	XtToolkitInitialize();
	mxtac=XtCreateApplicationContext();
	XtDisplayInitialize(mxtac, mpDisplay, "GUI_Motif", "GUI_Motif", NULL, 0, &argc, argv);


	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
	mpMutex = new(pthread_mutex_t);
	pthread_mutex_init(mpMutex,  &attr);
	mpGUI->RegisterCommand(msName+".AddWindow", AddWindowCB, this);

}

void GUI_Motif::start_thread()
{
  pthread_t t;
  pthread_create(&t, NULL, GUI_Motif_Thread_CB, this);
}

void GUI_Motif::RemoveWindow(Widget w)
{
	for(map<string,GUIWindowStruct>::iterator i = mmWindows.begin(); i!=mmWindows.end(); i++)
		if (i->second.wTopLevel == w)
		{
			DestroyWindow(i->first + ".Destroy");
			return;
		};
}


void GUI_Motif::RemoveWindowCB(Widget w, void* client_data, void* )
{
  ((GUI_Motif *) client_data)->RemoveWindow(w);
} 

void GUI_Motif::DestroyCB(void* ptr, string sCommand, string)
{
  ((GUI_Motif*)(ptr))->DestroyWindow(sCommand);
} 

void GUI_Motif::DestroyWindow(string sCommand)
{
  string sWindowName = sCommand;
  string sCommandSuffix(".Destroy");
  sWindowName.resize(sCommand.length() - sCommandSuffix.length());
  mpGUI->UnRegisterCommand(sWindowName + ".AddPushButton");
  mpGUI->UnRegisterCommand(sWindowName + ".AddToggleButton");
  mpGUI->UnRegisterCommand(sWindowName + ".AddMonitor");
  mpGUI->UnRegisterCommand(sWindowName + ".AddLabel");
  mpGUI->UnRegisterCommand(sWindowName + ".Destroy");
  XtUnrealizeWidget(mmWindows[sWindowName].wTopLevel);
  mmWindows[sWindowName].die=1;
  DoMotifEvents();
  
};

////////////////////////////////////////////////////////////////////////////////
//
// Push buttons
//

void GUI_Motif::AddPushButtonCB(void* ptr, string sCommand, string sParams)
{
  ((GUI_Motif*)(ptr))->AddPushButton(sCommand, sParams);
}


void GUI_Motif::AddPushButton(string sCommand, string sParams)
{
  
  vector<string> vs = ChopAndUnquoteString(sParams);
  if(vs.size()!=2) 
    {
      cout << "! GUI_Motif::AddPushButton: Need 2 params (name, command)." << endl;
      return;
    };
  


  string sWindowName = sCommand;
  string sCommandSuffix(".AddPushButton");
  sWindowName.resize(sCommand.length() - sCommandSuffix.length());
  Widget  w;
  
  Arg al[10];
  int ac = 0;
  char szName[1000];
  for(int i=0;i<1000;i++) szName[i]=0;
  vs[0].copy(szName,999);
  
  for(map<Widget,string>::iterator i = mmWindows[sWindowName].PushButtonMap.begin();
      i!=mmWindows[sWindowName].PushButtonMap.end();
      i++)
    if(i->second == vs[1])
      {
	cout << "? GUI_Motif::AddPushButton: Button with command \"" << vs[1] << "\" already there.. skipping. " << endl;
	return;
      }
  
  pthread_mutex_lock((pthread_mutex_t*)mpMutex);
  
  
  w = XmCreatePushButton(mmWindows[sWindowName].wRowCol, szName,al,ac);
  XtManageChild(w);
  mmWindows[sWindowName].PushButtonMap[w] = vs[1];
  XtAddCallback(w, XmNactivateCallback, ButtonHandlerCB, this);
  
  XWindowAttributes window_attributes_return;
  XGetWindowAttributes(mpDisplay, XtWindow(w), &window_attributes_return);
  int nButtonHeight = window_attributes_return.height;
  XGetWindowAttributes(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), &window_attributes_return);
  mmWindows[sWindowName].nWidth = (mmWindows[sWindowName].nWidth > window_attributes_return.width) ? mmWindows[sWindowName].nWidth : window_attributes_return.width;
  mmWindows[sWindowName].nHeight = (mmWindows[sWindowName].nHeight > window_attributes_return.height) ? mmWindows[sWindowName].nHeight : window_attributes_return.height;
  mmWindows[sWindowName].nHeight += (nButtonHeight + 3);
  XResizeWindow(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), 
		mmWindows[sWindowName].nWidth,
		mmWindows[sWindowName].nHeight);
  DoMotifEvents();
  pthread_mutex_unlock((pthread_mutex_t*)mpMutex);
  
}

////////////////////////////////////////////////////////////////////////////////
//
// Text input boxes
//

void GUI_Motif::AddSpinCB(void* ptr, string sCommand, string sParams)
{
  ((GUI_Motif*)(ptr))->AddSpin(sCommand, sParams);
}


void GUI_Motif::AddSpin(string sCommand, string sParams)
{
  
  vector<string> vs = ChopAndUnquoteString(sParams);
  if(vs.size() < 3 || vs.size() > 4)
    {
      cout << "! GUI_Motif::AddSpin: Need 3--4 params (gvar, min, max [, title])." << endl;
      return;
    };
  

  string sWindowName = sCommand;
  string sCommandSuffix(".AddSpin");
  sWindowName.resize(sCommand.length() - sCommandSuffix.length());
  Widget  w;
  
  Arg al[10];
  int ac = 0;
  char szName[1000];
  for(int i=0;i<1000;i++) szName[i]=0;
  vs[0].copy(szName,999);
  
  pthread_mutex_lock((pthread_mutex_t*)mpMutex);
  
  
  w = XmCreateTextField(mmWindows[sWindowName].wRowCol, szName,al,ac);
  mmWindows[sWindowName].SpinMap[w] = make_pair(vs[0], string(""));
  XtManageChild(w);

  XtAddCallback(w, XmNvalueChangedCallback, TextBoxCB, this);
  
  XWindowAttributes window_attributes_return;
  XGetWindowAttributes(mpDisplay, XtWindow(w), &window_attributes_return);
  int nButtonHeight = window_attributes_return.height;
  XGetWindowAttributes(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), &window_attributes_return);
  mmWindows[sWindowName].nWidth = (mmWindows[sWindowName].nWidth > window_attributes_return.width) ? mmWindows[sWindowName].nWidth : window_attributes_return.width;
  mmWindows[sWindowName].nHeight = (mmWindows[sWindowName].nHeight > window_attributes_return.height) ? mmWindows[sWindowName].nHeight : window_attributes_return.height;
  mmWindows[sWindowName].nHeight += (nButtonHeight + 3);
  XResizeWindow(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), 
		mmWindows[sWindowName].nWidth,
		mmWindows[sWindowName].nHeight);
  DoMotifEvents();
  pthread_mutex_unlock((pthread_mutex_t*)mpMutex);
  
}

void GUI_Motif::TextBoxCB(Widget w, XtPointer ptrMe, XtPointer xtpCall) 
{
  ((GUI_Motif*)ptrMe) -> TextBox(w, xtpCall);
}



// Handles the actual callback work
void GUI_Motif::TextBox(Widget w, XtPointer )
{
	for(map<string, GUIWindowStruct>::iterator i=mmWindows.begin(); i != mmWindows.end(); i++)
	{
 
		if(i->second.SpinMap.count(w))
		{
			char* c;
			XmTextPosition p;
			XtVaGetValues(w, XmNvalue, &c, XmNcursorPosition, &p, NULL);

			string cs(c);
			if(cs == "")
				cs = "0";

			p=1;

			GV3::set_var(i->second.SpinMap[w].first, cs.c_str());
			string s = GV3::get_var(i->second.SpinMap[w].first);

			if(s != c)
			{
				i->second.SpinMap[w].second = s;
				XtRemoveCallback(w, XmNvalueChangedCallback, TextBoxCB, this);
				p = min((int)p, (int)s.size());
				XtVaSetValues(w, XmNvalue, s.c_str(), XmNcursorPosition, p, NULL);
				XtAddCallback(w, XmNvalueChangedCallback, TextBoxCB, this);
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//
//

void GUI_Motif::AddToggleButtonCB(void* ptr, string sCommand, string sParams)
{
  ((GUI_Motif*)(ptr))->AddToggleButton(sCommand, sParams);
}


void GUI_Motif::AddToggleButton(string sCommand, string sParams)
{
  vector<string> vs = ChopAndUnquoteString(sParams);
  if(!((vs.size() == 2) || (vs.size()==3)))
    {
      cout << "! GUI_Motif::AddToggleButton: Need 2 or 3 params (button name, gvar2_int name, {true,false}=false )." << endl;
      return;
    };
  string sWindowName = sCommand;
  string sCommandSuffix(".AddToggleButton");
  sWindowName.resize(sCommand.length() - sCommandSuffix.length());

  for(map<Widget,toggleMapStruct>::iterator i = mmWindows[sWindowName].ToggleButtonMap.begin();
      i!=mmWindows[sWindowName].ToggleButtonMap.end();
      i++)
    if(i->second.sName == vs[0])
      {
	cout << "? GUI_Motif::AddToggleButton: Button with name \"" << vs[0] << "\" already there.. skipping. " << endl;
	return;
      }
  
  toggleMapStruct tms;
  tms.sName = vs[0];
  int nDefault = 0;
  if(vs.size()==3)
  {
    if(vs[2]=="true")
      nDefault = 1;
    else
      nDefault = 0;
  }

  mpGV2->Register(tms.gvn,vs[1],nDefault,true);
  tms.nCache = *tms.gvn;
  
 
  pthread_mutex_lock((pthread_mutex_t*)mpMutex);
  
  Widget w;
  Arg al[10];
  int ac = 0;
  char szName[1000];
  for(int i=0;i<1000;i++) szName[i]=0;
  vs[0].copy(szName,999);

  w = XmCreateToggleButton(mmWindows[sWindowName].wRowCol, szName,al,ac);
  XtManageChild(w);
  mmWindows[sWindowName].ToggleButtonMap[w] = tms;
  XtAddCallback(w, XmNvalueChangedCallback, ButtonHandlerCB, this);
  if(tms.nCache) XmToggleButtonSetState(w,true,true);
  
  XWindowAttributes window_attributes_return;
  XGetWindowAttributes(mpDisplay, XtWindow(w), &window_attributes_return);
  int nButtonHeight = window_attributes_return.height;
  XGetWindowAttributes(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), &window_attributes_return);
  mmWindows[sWindowName].nWidth = (mmWindows[sWindowName].nWidth > window_attributes_return.width) ? mmWindows[sWindowName].nWidth : window_attributes_return.width;
  mmWindows[sWindowName].nHeight = (mmWindows[sWindowName].nHeight > window_attributes_return.height) ? mmWindows[sWindowName].nHeight : window_attributes_return.height;
  mmWindows[sWindowName].nHeight += (nButtonHeight + 3);
  XResizeWindow(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), 
		mmWindows[sWindowName].nWidth,
		mmWindows[sWindowName].nHeight);
  DoMotifEvents();
  pthread_mutex_unlock((pthread_mutex_t*)mpMutex);
  
}

////////////////////////////////////////////////////////////////////////////////
//
// Monitor
// 
void GUI_Motif::AddMonitorCB(void* ptr, string sCommand, string sParams)
{
  ((GUI_Motif*)(ptr))->AddMonitor(sCommand, sParams);
}


void GUI_Motif::AddMonitor(string sCommand, string sParams)
{
  if(!mpDisplay)
    {
      cout << "! GUI_Motif::AddMonitor: GUI_Motif not initialised. " << endl;
      return;
    }
  vector<string> vs = ChopAndUnquoteString(sParams);
  if((vs.size() <2) || (vs.size() > 3))
    {
      cout << "! GUI_Motif::AddMonitor: Need 2 or 3 params: label, gvar name, num 20ms cycles to skip = 0 " << endl;
      return;
    };
  string sWindowName = sCommand;
  string sCommandSuffix(".AddMonitor");
  sWindowName.resize(sCommand.length() - sCommandSuffix.length());

  for(map<Widget,monitorMapStruct>::iterator i = mmWindows[sWindowName].MonitorMap.begin();
      i!=mmWindows[sWindowName].MonitorMap.end();
      i++)
    if(i->second.sLabel == vs[0])
      {
	cout << "? GUI_Motif::AddMontiro: Montor with Label \"" << vs[0] << "\" already there.. skipping. " << endl;
	return;
      }
  
  
  pthread_mutex_lock((pthread_mutex_t*)mpMutex);
  
  Widget w;
  Arg al[10];
  int ac = 0;
  w = XmCreateLabel(mmWindows[sWindowName].wRowCol,const_cast<char*>("(wait)"),al,ac);
  XtManageChild(w);
  monitorMapStruct &mms =  mmWindows[sWindowName].MonitorMap[w];
  mms.sLabel = vs[0];
  mms.sVarName = vs[1];
  if(vs.size()==3)
    {
      int* pn = ParseAndAllocate<int>(vs[2]);
      if (pn)
	{
	  mms.nDelaySetting = *pn;
	  delete pn;
	}
      else
	mms.nDelaySetting = 0;
    }
  else
    mms.nDelaySetting = 0;
  mms.nCurrentDelay=0;

  XWindowAttributes window_attributes_return;
  XGetWindowAttributes(mpDisplay, XtWindow(w), &window_attributes_return);
  int nButtonHeight = window_attributes_return.height;
  XGetWindowAttributes(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), &window_attributes_return);
  mmWindows[sWindowName].nWidth = (mmWindows[sWindowName].nWidth > window_attributes_return.width) ? mmWindows[sWindowName].nWidth : window_attributes_return.width;
  mmWindows[sWindowName].nHeight = (mmWindows[sWindowName].nHeight > window_attributes_return.height) ? mmWindows[sWindowName].nHeight : window_attributes_return.height;
  mmWindows[sWindowName].nHeight += (nButtonHeight + 3);
  XResizeWindow(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), 
		mmWindows[sWindowName].nWidth,
		mmWindows[sWindowName].nHeight);
  DoMotifEvents();
  pthread_mutex_unlock((pthread_mutex_t*)mpMutex);
  
}

////////////////////////////////////////////////////////////////////////////////
//
// Label
// 
void GUI_Motif::AddLabelCB(void* ptr, string sCommand, string sParams)
{
  ((GUI_Motif*)(ptr))->AddLabel(sCommand, sParams);
}


void GUI_Motif::AddLabel(string sCommand, string sParams)
{
  if(!mpDisplay)
    {
      cout << "! GUI_Motif::AddMonitor: GUI_Motif not initialised. " << endl;
      return;
    }
  vector<string> vs = ChopAndUnquoteString(sParams);
  if(vs.size() != 1)
    {
      cout << "! GUI_Motif::AddMonitor: Need 1 params: label " << endl;
      return;
    };
  string sWindowName = sCommand;
  string sCommandSuffix(".AddLabel");
  sWindowName.resize(sCommand.length() - sCommandSuffix.length());

  pthread_mutex_lock((pthread_mutex_t*)mpMutex);

  labels.push_back(vs[0]);
  
  Widget w;
  Arg al[10];
  int ac = 0;
  w = XmCreateLabel(mmWindows[sWindowName].wRowCol,const_cast<char*>(labels.back().c_str()),al,ac);
  XtManageChild(w);

  XWindowAttributes window_attributes_return;
  XGetWindowAttributes(mpDisplay, XtWindow(w), &window_attributes_return);
  int nButtonHeight = window_attributes_return.height;
  XGetWindowAttributes(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), &window_attributes_return);
  mmWindows[sWindowName].nWidth = (mmWindows[sWindowName].nWidth > window_attributes_return.width) ? mmWindows[sWindowName].nWidth : window_attributes_return.width;
  mmWindows[sWindowName].nHeight = (mmWindows[sWindowName].nHeight > window_attributes_return.height) ? mmWindows[sWindowName].nHeight : window_attributes_return.height;
  mmWindows[sWindowName].nHeight += (nButtonHeight + 3);
  XResizeWindow(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), 
		mmWindows[sWindowName].nWidth,
		mmWindows[sWindowName].nHeight);
  DoMotifEvents();
  pthread_mutex_unlock((pthread_mutex_t*)mpMutex);
  
}


void GUI_Motif::AddSliderCB(void* ptr, string sCommand, string sParams)
{
  ((GUI_Motif*)(ptr))->AddSlider(sCommand, sParams);
}


void GUI_Motif::AddSlider(string sCommand, string sParams)
{
  if(!mpDisplay)
    {
      cout << "! GUI_Motif::AddSlider: GUI_Motif not initialised. " << endl;
      return;
    }
  vector<string> vs = ChopAndUnquoteString(sParams);
  if((vs.size() != 3))
    {
      cout << "! GUI_Motif::AddSlider: 3 params: gvar name, min, max " << endl;
      return;
    };
  string sWindowName = sCommand;
  string sCommandSuffix(".AddSlider");
  sWindowName.resize(sCommand.length() - sCommandSuffix.length());
  for(map<Widget,sliderMapStruct>::iterator i = mmWindows[sWindowName].SliderMap.begin();
      i!=mmWindows[sWindowName].SliderMap.end();
      i++)
    if(i->second.sVarName == vs[0])
      {
	cout << "? GUI_Motif::AddSlider: Slider with gvar \"" << vs[0] << "\" already there.. skipping. " << endl;
	return;
      }

  sliderMapStruct sms;
  sms.sVarName = vs[0];  
  double *pd;
  pd = ParseAndAllocate<double>(vs[1]);
  if(!pd)
    {
      cout << "? GUI_Motif::AddSlider: Could not parse min range parameter \"" << vs[1] << "\"."<< endl;
      return;
    }
  sms.dMin = *pd;
  delete pd;
  pd=ParseAndAllocate<double>(vs[2]);
  if(!pd)
    {
      cout << "? GUI_Motif::AddSlider: Could not parse max range parameter \"" << vs[2] << "\"."<< endl;
      return;
    } 
  sms.dMax=*pd;
  delete pd;
  if(sms.dMax == sms.dMin)
    {
      cout << "? GUI_Motif::AddSlider: Cannot have max = min! " << endl;
      return;
    }

  
  
  pthread_mutex_lock((pthread_mutex_t*)mpMutex);
  
  Widget w;
  Arg al[10];
  int ac = 0;

  XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);  ac++;  
  //  XtSetArg(al[ac], XmNsliderMark, XmETCHED_LINE);  ac++;  
  XtSetArg(al[ac], XmNwidth, mmWindows[sWindowName].nWidth - 5);  ac++;  
  XtSetArg(al[ac], XmNmaximum, 310);  ac++;  
  XtSetArg(al[ac], XmNsliderSize, 10);  ac++;  
  
  w = XmCreateScrollBar(mmWindows[sWindowName].wRowCol,const_cast<char*>("SliderName"),al,ac);
  XtManageChild(w);
  XtAddCallback(w, XmNdragCallback, ButtonHandlerCB, this);
  XtAddCallback(w, XmNvalueChangedCallback, ButtonHandlerCB, this);

  mmWindows[sWindowName].SliderMap[w] = sms;
  
  //   if(vs.size()==3)
//     {
//       int* pn = ParseAndAllocate<int>(vs[2]);
//       if (pn)
// 	{
// 	  mms.nDelaySetting = *pn;
// 	  delete pn;
// 	}
//       else
// 	mms.nDelaySetting = 0;
//     }
//   else
//     mms.nDelaySetting = 0;
//   mms.nCurrentDelay=0;
  
  XWindowAttributes window_attributes_return;
  XGetWindowAttributes(mpDisplay, XtWindow(w), &window_attributes_return);
  int nButtonHeight = window_attributes_return.height;
  XGetWindowAttributes(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), &window_attributes_return);
  mmWindows[sWindowName].nWidth = (mmWindows[sWindowName].nWidth > window_attributes_return.width) ? mmWindows[sWindowName].nWidth : window_attributes_return.width;
  mmWindows[sWindowName].nHeight = (mmWindows[sWindowName].nHeight > window_attributes_return.height) ? mmWindows[sWindowName].nHeight : window_attributes_return.height;
  mmWindows[sWindowName].nHeight += (nButtonHeight + 3);
  XResizeWindow(mpDisplay, XtWindow(mmWindows[sWindowName].wTopLevel), 
		mmWindows[sWindowName].nWidth,
		mmWindows[sWindowName].nHeight);
  DoMotifEvents();
  pthread_mutex_unlock((pthread_mutex_t*)mpMutex);
  
}



// Callback function for MOTIF events, wrapper for button_F

void GUI_Motif::ButtonHandlerCB(Widget w, XtPointer ptrMe, XtPointer xtpCall) 
{
  ((GUI_Motif*)ptrMe) -> ButtonHandler(w, xtpCall);
}


// Handles the actual callback work
void GUI_Motif::ButtonHandler(Widget w, XtPointer xtpCall)
{
  
  for(map<string, GUIWindowStruct>::iterator i=mmWindows.begin();i!=mmWindows.end();i++)
    {
      if(i->second.PushButtonMap.count(w)>0)
	{
	  mpGUI->ParseLine(i->second.PushButtonMap[w]);
	}
      else if(i->second.ToggleButtonMap.count(w)>0)
	{
	  *i->second.ToggleButtonMap[w].gvn = i->second.ToggleButtonMap[w].nCache = ((XmToggleButtonCallbackStruct*) xtpCall)->set;
	}
      else if(i->second.SliderMap.count(w)>0)
	{
	  sliderMapStruct &sms =  i->second.SliderMap[w];
	  double dRawValue = (double) (((XmScrollBarCallbackStruct*)xtpCall)->value);
	  ostringstream ost;
	  ost.str()="";
	  ost << (dRawValue / 300.0) * (sms.dMax-sms.dMin) + sms.dMin;
	  mpGV2->SetVar(sms.sVarName, ost.str(), 1);
	  sms.sCachedValue = GV3::get_var(sms.sVarName);

	}
    };
  
}

void GUI_Motif::GUI_Motif_Thread()
{
	while(mpDisplay)
	{
		poll();
		usleep(20000);
	}
}

void GUI_Motif::poll()
{
	if(!mpDisplay)
		return;
  static char szString[1000];

  pthread_mutex_lock(mpMutex);
  for(map<string,GUIWindowStruct>::iterator w=mmWindows.begin();w!=mmWindows.end();w++)
    {
      for(map< Widget, toggleMapStruct >::iterator i = w->second.ToggleButtonMap.begin(); i!=w->second.ToggleButtonMap.end(); i++)
	{
	  if(*(i->second.gvn)!=(i->second.nCache))
	    {
	      i->second.nCache = *(i->second.gvn);
	      if(i->second.nCache)
		XmToggleButtonSetState(i->first,true,false);
	      else
		XmToggleButtonSetState(i->first,false,false);
	    };
	}
      
      for(map < Widget, monitorMapStruct>::iterator i = w->second.MonitorMap.begin(); i!=w->second.MonitorMap.end(); i++)
	{
	  if( (i->second.nCurrentDelay--) >0)
	    continue;
	  i->second.nCurrentDelay=i->second.nDelaySetting;	  
	  ostringstream os;
	  os.str()="";
	  os << i->second.sLabel << ":" << mpGV2->StringValue(i->second.sVarName);
	  if(os.str() == i->second.sCache)
	    continue;
	  i->second.sCache=os.str();
	  strncpy(szString,os.str().c_str(),999);
	  Arg al;
	  XmString xmstr;
	  xmstr = XmStringCreate(szString, XmFONTLIST_DEFAULT_TAG);
	  XtSetArg (al, XmNlabelString, xmstr);
	  XtSetValues(i->first, &al, 1);
	  XmStringFree(xmstr);
	};

      for(map < Widget, sliderMapStruct>::iterator i = w->second.SliderMap.begin(); i!=w->second.SliderMap.end(); i++)
	{
	  sliderMapStruct &sms = i->second;
	  string sNewValue = GV3::get_var(sms.sVarName);
	  if(sNewValue == sms.sCachedValue)
	    continue;
	  sms.sCachedValue = sNewValue;

	  double pdNewValue;
	  int err = GVars3::serialize::from_string(sNewValue, pdNewValue);

	  if(err)
	  	continue;

	  sms.dMax = max(sms.dMax, pdNewValue);
	  sms.dMin = min(sms.dMin, pdNewValue);

	  double dFraction = ((pdNewValue - sms.dMin)/ (sms.dMax-sms.dMin));

	  XmScrollBarSetValues(i->first, (int)(300 * dFraction), 0, 0, 0, False);
	};


       for(map<Widget, pair<string, string> >::iterator i=w->second.SpinMap.begin(); i != w->second.SpinMap.end(); i++)
       {
	    if(i->second.second != GV3::get_var(i->second.first))
	    {
		  XmTextPosition p;
		  XtVaGetValues(i->first, XmNcursorPosition, &p, NULL);

		  string s = GV3::get_var(i->second.first);

		  i->second.second = s;
		  XtRemoveCallback(i->first, XmNvalueChangedCallback, TextBoxCB, this);
		  p = min((int)p, (int)s.size());
		  XtVaSetValues(i->first, XmNvalue, s.c_str(), XmNcursorPosition, p, NULL);
		  XtAddCallback(i->first, XmNvalueChangedCallback, TextBoxCB, this);
	    }
       }

      
    };
  DoMotifEvents();
  
  //Now kill any windows in need of killing
  for(map<string,GUIWindowStruct>::iterator tmp, w=mmWindows.begin();w!=mmWindows.end();)
  {
  	tmp = w;
	w++;
	if(tmp->second.die)
		mmWindows.erase(tmp);
  }

  DoMotifEvents();
} 
void* GUI_Motif::GUI_Motif_Thread_CB(void* ptr)
{
  ((GUI_Motif*) ptr)->GUI_Motif_Thread();
  return 0;
}

void GUI_Motif::DoMotifEvents()
{
  XEvent xevent;
  int num_events=0;
  while(XtAppPending(mxtac) && num_events < 100000){
    XtAppNextEvent(mxtac,&xevent);
    XtDispatchEvent(&xevent);
    num_events++;
  }
}

//Instantiations, link time virtual function definitions 
class GUI_Motif GUI_Motif_instance(&GUI, &GV2);

void GUIWidgets::process_in_crnt_thread()
{
	GUI_Motif_instance.poll();
}

void GUIWidgets::start_thread()
{
	GUI_Motif_instance.start_thread();
}

}
