#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <malloc.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/TextF.h>
#include <Xm/PushB.h>
#include "TextField.h"

#define dprintf if (debug) fprintf

const char *const TextField::acceptCallback = "acceptCallback";
const Boolean TextField::NotEditable = False;

TextField::TextField(const char *name)
  : VkComponent(name)
{
}


TextField::TextField(const char *name, Widget parent, Boolean editable) :
  VkComponent(name)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  installDestroyHandler();
  
  _text = XtVaCreateManagedWidget("text", xmTextFieldWidgetClass, _baseWidget,
				  XmNleftAttachment, XmATTACH_FORM,
				  XmNtopAttachment, XmATTACH_FORM,
				  NULL);

  if (editable)
  {
    XtAddCallback(_text, 
		  XmNmodifyVerifyCallback,
		  &TextField::typingCallback,
		  (XtPointer )this);

    // Carriage return means accept input
    XtAddCallback(_text, XmNactivateCallback,
		  &TextField::okCallback, (XtPointer )this);
  
  _buttonMgr = 
    XtVaCreateWidget("buttonMgr", xmRowColumnWidgetClass, _baseWidget,
		     XmNpacking, XmPACK_COLUMN,
		     XmNnumColumns, 2,
		     XmNorientation, XmVERTICAL,
		     XmNleftAttachment, XmATTACH_WIDGET,
		     XmNleftWidget, _text,
		     XmNtopAttachment, XmATTACH_FORM,
		     XmNrightAttachment, XmATTACH_FORM,
		     XmNbottomAttachment, XmATTACH_FORM,
		     NULL);
  
  
    _ok = 
      XtVaCreateManagedWidget("ok", xmPushButtonWidgetClass, _buttonMgr, NULL);

    XtAddCallback(_ok, XmNactivateCallback,
		  &TextField::okCallback, (XtPointer )this);

    _cancel = 
      XtVaCreateManagedWidget("cancel", xmPushButtonWidgetClass, _buttonMgr, 
			      NULL);
  
    XtAddCallback(_cancel, XmNactivateCallback,
		  &TextField::cancelCallback, (XtPointer )this);

    XtSetMappedWhenManaged(_buttonMgr, False);
    XtManageChild(_buttonMgr);
  }
  else // Not editable
  {
    XtVaSetValues(_text,
		  XmNeditable, False,
		  XmNcursorPositionVisible, False,
		  XmNshadowThickness, 0,
		  XmNhighlightThickness, 0,
		  XmNhighlightOnEnter, False,
		  NULL);
  }

  _prevString = 0;
}


TextField::~TextField()
{
}


void TextField::set(char *str)
{
  XmTextFieldSetString(_text, str);
  makeBackup(str);
}


const char *TextField::get()
{
  static char *ptr;
  XtVaGetValues(_text, XmNvalue, &ptr, NULL);
  return (const char *)ptr;
}


void TextField::setColumns(int nColumns)
{
  XtVaSetValues(_text, XmNcolumns, nColumns, NULL);
}


void TextField::typingCallback(Widget w, 
			       XtPointer clientData,
			       XtPointer callData)
{
  XmTextVerifyCallbackStruct *cb = (XmTextVerifyCallbackStruct *)callData;
  
  if (cb->event == NULL)
    // Callback was invoked by text being set programatically
    return;
  
  TextField *obj = (TextField *)clientData;

  if ( cb->text->ptr != NULL )
  {
    for (int len = 0; len < cb->text->length; len++ )
      if ( !isprint(cb->text->ptr[len]) )
	cb->doit = False;
  }
  
  obj->showButtons();
}


void TextField::okCallback(Widget w, 
			   XtPointer clientData,
			   XtPointer callData)
{
  Boolean debug = False;
  
  XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)callData;
  dprintf(stderr, "cbs->reason=%d\n", cbs->reason);
  
  // Hide buttons
  TextField *obj = (TextField *)clientData;
  obj->hideButtons();
  obj->accept();
} 


void TextField::cancelCallback(Widget w, 
			       XtPointer clientData,
			       XtPointer callData)
{
  // Hide buttons
  TextField *obj = (TextField *)clientData;

  obj->hideButtons();
  
  obj->cancel();
}


void TextField::accept()
{
  callCallbacks(acceptCallback, (void *)Accept);
  makeBackup(get());
}


void TextField::cancel()
{
  if (_prevString)
    // Restore previous string
    set(_prevString);
}


void TextField::showButtons()
{
  if (XtIsRealized(_buttonMgr))
    XtMapWidget(_buttonMgr);
}  


void TextField::hideButtons()
{
  if (XtIsRealized(_buttonMgr))
    XtUnmapWidget(_buttonMgr);
}



void TextField::makeBackup(const char *string)
{
  free((void *)_prevString);
  _prevString = strdup(string);
}
