/**
*** XPG - Graphical User Interface for Postgres
*** Copyright (C) 1993  Ranen Goren (ranen@cs.huji.ac.il).

*** This program is free software; you can redistribute it and/or modify
*** it under the terms of the GNU General Public License as published by
*** the Free Software Foundation; either version 2 of the License, or
*** (at your option) any later version.

*** This program is distributed in the hope that it will be useful,
*** but WITHOUT ANY WARRANTY; without even the implied warranty of
*** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*** GNU General Public License for more details.

*** You should have received a copy of the GNU General Public License
*** along with this program; if not, write to the Free Software
*** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**/


#include <stdio.h>
#include <string.h>
#include <varargs.h>
#include <X11/StringDefs.h>
#include <X11/Intrinsic.h> 
#include <X11/cursorfont.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/LabelG.h>
#include <Xm/Label.h>
#include <Xm/TextF.h>
#include <Xm/Text.h>
#include <Xm/Separator.h>
#include <Xm/ScrolledW.h>
#include "xpg.h"

#define String JUST_A_DUMB_STRING
#include "tmp/libpq.h"       /* postgres */
#undef String

#ifdef MEM_DEBUG
#include "/CS/system/ranen/Src/Lib/Malloc/malloc.h"
#endif

typedef struct 
{
    relInfo *rel;
    Widget opsSh;
    int numFields;
    Widget *values;
} opsCbsStruct;




char *xpgPQexec();
char *execAndWarn();
void helpView();
Widget openClassOpsShell();
void checkField();
void checkInputChar();
void classOpsCancel();
void classAppendOk();
void classReplaceOk();
void classDeleteOk();


/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
Widget openClassOpsShell(parent, name)
  Widget parent;
  char *name;
{
    Widget sh;
    
    sh = XtVaCreatePopupShell(name, topLevelShellWidgetClass, parent, NULL);
    setWidgetPos(sh, parent, 0.5, 0, 0.5, 0);
    return sh;
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
createOpsWidgets(rel, shName, okCB, cancelCB)
  relInfo *rel;
  String shName;
  void (*okCB)(), (*cancelCB)();
{
    Widget sh, sw=NULL, fieldsForm, text_w, form, copBox, ok, cancel;
    Widget top;
    XmString label;
    PortalBuffer *p;
    int i, m, n;
    Widget *values;
    Widget ignore, sel, selDup;
    opsCbsStruct *cbs;
    char *copTextName;
    
    setCursor(rel->top, XC_watch);
    p = PQparray(rel->portal);
    n = PQntuples(p);
    if (rel->attrInfo)
	free(rel->attrInfo);
    if (!(m = getFieldInfo(rel, &(rel->attrInfo), IGNORE_LENGTH, 
			   CONSIDER_FIELD_NAMES)))
	return 1;
    if (((values = (Widget *)calloc(m+2, sizeof(char *))) == NULL) ||
	((cbs = (opsCbsStruct *)malloc(sizeof(opsCbsStruct))) == NULL))
	    XtError("Mem error!");
    cbs->rel = rel;
    cbs->numFields = m;
    cbs->values = values;
    cbs->opsSh = sh = openClassOpsShell(rel->top, shName);
    copBox = XtVaCreateManagedWidget("copBox",
				     xmFormWidgetClass, sh, NULL);
    XtAddCallback(copBox, XmNhelpCallback, helpView, "class operations");
    if (strcmp(shName, "deleteSh"))
    {
	sw = XtVaCreateManagedWidget("copSW", xmScrolledWindowWidgetClass, 
				     copBox,
				     XmNscrollingPolicy,  XmAUTOMATIC,
				     XmNvisualPolicy, XmVARIABLE,
				     NULL);
	
	fieldsForm = XtVaCreateManagedWidget("copFields", xmFormWidgetClass,sw,
					     NULL);
	top = NULL;
    }
    for (i = ATTR_1ST(rel); strcmp(shName, "deleteSh") && i<m; i++) 
    {
        form = XtVaCreateWidget("copForm", xmFormWidgetClass, fieldsForm,
				XmNfractionBase,  10,
				XmNleftAttachment,   XmATTACH_FORM,
				XmNrightAttachment,  XmATTACH_FORM,
				XmNtopAttachment,    
				(top ? XmATTACH_WIDGET : XmATTACH_FORM),
				XmNtopWidget,        top,
				NULL);
	top = form;  /* for the next widget */
	label = XmStringCreateSimple(rel->attrInfo[i].name);
        XtVaCreateManagedWidget("copLabel",
				xmLabelGadgetClass, form,
				XmNlabelString,      label,
				XmNtopAttachment,    XmATTACH_FORM,
				XmNbottomAttachment, XmATTACH_FORM,
				XmNleftAttachment,   XmATTACH_FORM,
				XmNrightAttachment,  XmATTACH_POSITION,
				XmNrightPosition,    3,
				XmNalignment,        XmALIGNMENT_END,
				NULL);
	XmStringFree(label);
	copTextName = (rel->attrInfo[i].pgType==702 ? "copTextDate" 
		       : "copText");
        text_w = XtVaCreateManagedWidget(copTextName,
					 xmTextFieldWidgetClass, form,
					 XmNuserData,        rel,
					 XmNtraversalOn,     True,
					 XmNverifyBell,      False,
					 XmNrightAttachment, XmATTACH_FORM,
					 XmNleftAttachment,  XmATTACH_POSITION,
					 XmNleftPosition,    4,
					 NULL);
	values[i] = text_w;
/* printf("%s  %d\n", rel->attrInfo[i].name, rel->attrInfo[i].pgType); */
	XtAddCallback(text_w, XmNmodifyVerifyCallback,
		      checkInputChar, rel->attrInfo[i].pgType);
        XtAddCallback(text_w, XmNactivateCallback, checkField, NULL);
        XtAddCallback(text_w, XmNactivateCallback,
		      XmProcessTraversal, XmTRAVERSE_NEXT_TAB_GROUP);
        XtManageChild(form);
    }

    for (i=1; i<=2; i++)
    {
	if (i>1 || strcmp(shName, "deleteSh"))
	    XtVaCreateManagedWidget("copSeparator", xmSeparatorWidgetClass, 
				    copBox, NULL);
	form = XtVaCreateWidget("copForm", xmFormWidgetClass, copBox, 
				XmNfractionBase,  10,
				NULL);
	label = XmStringCreateSimple(i == 1 ? "From" : "Where");
        XtVaCreateManagedWidget("copLabel",
				xmLabelGadgetClass, form,
				XmNlabelString,      label,
				XmNtopAttachment,    XmATTACH_FORM,
				XmNbottomAttachment, XmATTACH_FORM,
				XmNleftAttachment,   XmATTACH_FORM,
				XmNrightAttachment,  XmATTACH_POSITION,
				XmNrightPosition,    2,
				XmNalignment,        XmALIGNMENT_END,
				NULL);
	XmStringFree(label);
        text_w = XtVaCreateManagedWidget("copText",
					 xmTextFieldWidgetClass, form,
					 XmNtraversalOn,     True,
					 XmNrightAttachment, XmATTACH_FORM,
					 XmNleftAttachment,  XmATTACH_POSITION,
					 XmNleftPosition,    3,
					 NULL);
	if (!strcmp(shName, "deleteSh"))
	    values[i] = text_w;
	else
	    values[m-1+i] = text_w;
        XtAddCallback(text_w, XmNactivateCallback, checkField, NULL);
        XtAddCallback(text_w, XmNlosePrimaryCallback, checkField, 
		      &values[m-1+i]);
        XtAddCallback(text_w, XmNactivateCallback,
		      XmProcessTraversal, XmTRAVERSE_NEXT_TAB_GROUP);
	XtManageChild(form);
    }
    XtVaCreateManagedWidget("copSeparator", xmSeparatorWidgetClass, copBox, 
			    NULL);
    if (strcmp(shName, "appendSh"))
    {
	XtVaCreateManagedWidget("copSelLabel", xmLabelGadgetClass, copBox, 
				NULL);
	form = XtVaCreateWidget("copForm", xmFormWidgetClass, copBox, NULL);
	sel = XtVaCreateManagedWidget("copSel", 
				      xmPushButtonWidgetClass, form,
				      XmNuserData,        "sel",
				      XmNleftAttachment,  XmATTACH_FORM,
				      XmNtopAttachment,   XmATTACH_FORM,
				      XmNbottomAttachment,XmATTACH_FORM,
				      XmNrightAttachment, XmATTACH_POSITION,
				      XmNrightPosition,   33,
				      NULL);
	XtAddCallback(sel, XmNactivateCallback, okCB, cbs);
	selDup = XtVaCreateManagedWidget("copSelDup", 
					 xmPushButtonWidgetClass, form,
					 XmNuserData,        "selDup",
					 XmNrightAttachment, XmATTACH_POSITION,
					 XmNrightPosition,   67,
					 XmNleftAttachment,  XmATTACH_WIDGET,
					 XmNleftWidget,      sel,
					 XmNtopAttachment,   XmATTACH_FORM,
					 XmNbottomAttachment,XmATTACH_FORM,
					 NULL);
	XtAddCallback(selDup, XmNactivateCallback, okCB, cbs);
	ignore = XtVaCreateManagedWidget("copIgnore", 
					 xmPushButtonWidgetClass, form,
					 XmNuserData,        "ignore",
					 XmNleftAttachment,  XmATTACH_WIDGET,
					 XmNleftWidget,      selDup,
					 XmNrightAttachment, XmATTACH_FORM,
					 XmNtopAttachment,   XmATTACH_FORM,
					 XmNbottomAttachment,XmATTACH_FORM,
					 NULL);
	XtAddCallback(ignore, XmNactivateCallback, okCB, cbs);
	XtManageChild(form);
    }
    form = XtVaCreateWidget("copForm", xmFormWidgetClass, copBox, 
			    NULL);
    if (!strcmp(shName, "appendSh"))
    {
	ok = XtVaCreateManagedWidget("copOk", 
				     xmPushButtonWidgetClass, form,
				     XmNrightAttachment, XmATTACH_POSITION,
				     XmNrightPosition,   50,
				     XmNleftAttachment,  XmATTACH_FORM,
				     XmNtopAttachment,   XmATTACH_FORM,
				     XmNbottomAttachment,XmATTACH_FORM,
				     NULL);
	XtAddCallback(ok, XmNactivateCallback, okCB, cbs);
    }
    else
	ok = NULL;
    cancel = XtVaCreateManagedWidget("copCancel", 
				     xmPushButtonWidgetClass, form,
				     XmNrightAttachment, XmATTACH_FORM,
				     XmNleftAttachment,  
				     (ok ? XmATTACH_WIDGET : XmATTACH_FORM),
				     XmNleftWidget,      ok,
				     XmNtopAttachment,   XmATTACH_FORM,
				     XmNbottomAttachment,XmATTACH_FORM,
				     NULL);
    XtAddCallback(cancel, XmNactivateCallback, cancelCB, cbs);
    XtManageChild(form);
    setFormColumn(copBox, -1);
    XtManageChild(copBox);
    XtPopup(sh, XtGrabNone);  
    setCursor(rel->top, None);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
void classOpsCancel(w, opsCbs, cbs)
  Widget w;
  opsCbsStruct *opsCbs;
  XtPointer cbs;
{
    free(opsCbs->values);
    XtDestroyWidget(opsCbs->opsSh);
    free(opsCbs);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
classAppend(rel)
  relInfo *rel;
{
    if (!rel->perms.append && !rel->perms.write)
    {
	warn(rel->top, "Write access to class denied!");
	return;
    }
    createOpsWidgets(rel, "appendSh", classAppendOk, classOpsCancel);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
void classAppendOk(w, opsCbs, cbs)
  Widget w;
  opsCbsStruct *opsCbs;
  XtPointer cbs;
{
    int i, m;
    Widget *values;
    char *value, *p;
    char buf[MAX_PG_CMD];
    Boolean isFirst = True;
    PortalBuffer *portal;
    relInfo *rel = opsCbs->rel;
    char *pgResult;
    
    portal = PQparray(rel->portal);
    values = opsCbs->values;
    m = opsCbs->numFields;
    sprintf(buf, "append %s (", rel->class);
    for (i=ATTR_1ST(rel); i<m; i++)
    {
	value = XmTextFieldGetString(values[i]);
	for (p=value; *p!='\0' && *p==' '; p++)
	    ;
	if (*p != '\0')
	{
	    if (!isFirst)
		strcat(buf, ", ");
	    strcat(buf, rel->attrInfo[i].name);
	    strcat(buf, "=");
	    strcat(buf, value);
	    isFirst = False;
	    XtFree(value);
	}
    }
    strcat(buf, ")");

    value = XmTextFieldGetString(values[m]);   /* 'From' line */
    for (p=value; *p!='\0' && *p==' '; p++)
	;
    if (*p != '\0')
    {
	strcat(buf, " from ");
	strcat(buf, value);
    }	
    XtFree(value);

    value = XmTextFieldGetString(values[m+1]);   /* 'Where' line */
    for (p=value; *p!='\0' && *p==' '; p++)
	;
    if (*p != '\0')
    {
	strcat(buf, " where ");
	strcat(buf, value);
    }	
    XtFree(value);

    logText(False, "(append dialog)  %s\n", buf);
    setBusy(rel, SEND);
    pgResult = execAndWarn(GetTopShell(w), buf, "The append command failed!");
    if (pgResult[0] != 'R')
    {
	/* classOpsCancel(w, opsCbs, NULL); */
	retrieveClass(rel, True, False, False);
    }
    else
	logText(False, "%s\n", pgResult+1);
    setBusy(rel, NOBUSY);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
classReplace(rel)
  relInfo *rel;
{
    if (!rel->perms.write)
    {
	warn(rel->top, "Write access to class denied!");
	return;
    }
    createOpsWidgets(rel, "replaceSh", classReplaceOk, classOpsCancel);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
void classReplaceOk(w, opsCbs, cbs)
  Widget w;
  opsCbsStruct *opsCbs;
  XtPointer cbs;
{
    relInfo *rel = opsCbs->rel;
    int i, m;
    Widget *values;
    char *value, *p;
    char *action;
    char buf[MAX_PG_CMD];
    Boolean isFirst = True;
    PortalBuffer *portal;
    int posCount, pos;
    int *posList;
    Boolean whereMissing;
    Boolean selectionsFound=True;   /* until otherwise proven */
    char *pgResult;
 
    
    XtVaGetValues(w, XmNuserData, &action, NULL);  /* userData stores action */
    portal = PQparray(rel->portal);
    values = opsCbs->values;
    m = opsCbs->numFields;
    sprintf(buf, "replace %s (", rel->class);
    for (i=ATTR_1ST(rel); i<m; i++)
    {
	value = XmTextFieldGetString(values[i]);
	for (p=value; *p!='\0' && *p==' '; p++)
	    ;
	if (*p != '\0')
	{
	    if (!isFirst)
		strcat(buf, ", ");
	    strcat(buf, PQfname(portal,0,i));
	    strcat(buf, "=");
	    strcat(buf, value);
	    isFirst = False;
	    XtFree(value);
	}
    }
    strcat(buf, ")");

    value = XmTextFieldGetString(values[m]);   /* 'From' line */
    for (p=value; *p!='\0' && *p==' '; p++)
	;
    if (*p != '\0')
    {
	strcat(buf, " from ");
	strcat(buf, value);
    }	
    XtFree(value);

    whereMissing = True;    /* default */
    value = XmTextFieldGetString(values[m+1]);   /* 'Where' line */
    for (p=value; *p!='\0' && *p==' '; p++)
	;
    if (*p != '\0')
    {
	strcat(buf, " where (");
	strcat(buf, value);
	strcat(buf, ")");
	whereMissing = False;
    }	
    XtFree(value);

    if (strcmp(action, "ignore") && (selectionsFound = 
		XmListGetSelectedPos(rel->records, &posList, &posCount)))
    {
	if (whereMissing)
	    strcat(buf, " where (");
	else
	    strcat(buf, " and (");
	p = buf + strlen(buf);   /* store the original end of string */
	for (pos=0; pos<posCount; pos++)
	{
	    if (!strcmp(action, "sel"))
		sprintf(p, "%s.oid=\"%s\"", rel->class, 
			PQgetvalue(portal,posList[pos]-1,0));
	    else
		buildMatchingQual(p, rel, portal, rel->class, m, 
				  posList[pos]-1);
	    strcat(buf, ")");
	    logText(False, "(replace dialog) %s\n", buf);
	    setBusy(rel, SEND);
	    pgResult = execAndWarn(GetTopShell(w), buf, 
				   "One of the replace commands failed!");
	    if (pgResult[0] == 'R')
	    {
		logText(False, "%s\n", pgResult+1);
		setBusy(rel, NOBUSY);
		return;
	    }
	}
	/* classOpsCancel(w, opsCbs, NULL); */
	retrieveClass(rel, True, False, False);
	return;
    }
    if (! selectionsFound)
    {
	warn(XtParent(XtParent(XtParent(w))), "There are no selected tuples!");
	return;
    }
    logText(False, "(replace dialog) %s\n", buf);
    setBusy(rel, SEND);
    pgResult = execAndWarn(GetTopShell(w), buf, "The replace command failed!");
    if (pgResult[0] != 'R')
    {
	/* classOpsCancel(w, opsCbs, NULL); */
	retrieveClass(rel, True, False, False);
    }	
    else
	logText(False, "%s\n", pgResult+1);
    setBusy(rel, NOBUSY);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
classDelete(rel)
  relInfo *rel;
{
    if (!rel->perms.write)
    {
	warn(rel->top, "Write access to class denied!");
	return;
    }
    createOpsWidgets(rel, "deleteSh", classDeleteOk, classOpsCancel);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
void classDeleteOk(w, opsCbs, cbs)
  Widget w;
  opsCbsStruct *opsCbs;
  XtPointer cbs;
{
    relInfo *rel = opsCbs->rel;
    int i, m;
    Widget *values;
    char *value, *p;
    char *action;
    char buf[MAX_PG_CMD];
    PortalBuffer *portal;
    int posCount, pos;
    int *posList;
    Boolean whereMissing;
    Boolean selectionsFound=True;   /* until otherwise proven */
    char *pgResult;
 
    
    XtVaGetValues(w, XmNuserData, &action, NULL);  /* userData stores action */
    portal = PQparray(rel->portal);
    values = opsCbs->values;
    m = opsCbs->numFields;
    sprintf(buf, "delete %s", rel->class);

    value = XmTextFieldGetString(values[1]);   /* 'From' line */
    for (p=value; *p!='\0' && *p==' '; p++)
	;
    if (*p != '\0')
    {
	strcat(buf, " from ");
	strcat(buf, value);
    }	
    XtFree(value);

    whereMissing = True;    /* default */
    value = XmTextFieldGetString(values[2]);   /* 'Where' line */
    for (p=value; *p!='\0' && *p==' '; p++)
	;
    if (*p != '\0')
    {
	strcat(buf, " where (");
	strcat(buf, value);
	strcat(buf, ")");
	whereMissing = False;
    }	
    XtFree(value);

    if (strcmp(action, "ignore") && (selectionsFound = 
		XmListGetSelectedPos(rel->records, &posList, &posCount)))
    {
	if (whereMissing)
	    strcat(buf, " where (");
	else
	    strcat(buf, " and (");
	p = buf + strlen(buf);   /* store the original end of string */
	for (pos=0; pos<posCount; pos++)
	{
	    if (!strcmp(action, "sel"))
		sprintf(p, "%s.oid=\"%s\"", rel->class, 
			PQgetvalue(portal,posList[pos]-1,0));
	    else
		buildMatchingQual(p, rel, portal, rel->class, m, 
				  posList[pos]-1);
	    strcat(buf, ")");
	    logText(False, "(delete dialog) %s\n", buf);
	    setBusy(rel, SEND);
	    pgResult = execAndWarn(GetTopShell(w), buf, 
				   "One of the delete commands failed!");
	    if (pgResult[0] == 'R')
	    {
		logText(False, "%s\n", pgResult+1);
		setBusy(rel, NOBUSY);
		return;
	    }
	}
	/* classOpsCancel(w, opsCbs, NULL); */
	retrieveClass(rel, False, False, False);
	return;
    }
    if (! selectionsFound)
    {
	warn(XtParent(XtParent(XtParent(w))), "There are no selected tuples!");
	return;
    }
    logText(False, "(delete dialog)  %s\n", buf);
    setBusy(rel, SEND);
    pgResult = execAndWarn(GetTopShell(w), buf, "The delete command failed!");
    if (pgResult[0] != 'R')
    {
	/* classOpsCancel(w, opsCbs, NULL); */
	retrieveClass(rel, False, False, False);
    }	
    else
	logText(False, "%s\n", pgResult+1);
    setBusy(rel, NOBUSY);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
buildMatchingQual(buf, rel, portal, class, m, i)
  char *buf;               /* destination        */
  relInfo *rel;
  PortalBuffer *portal;
  char *class;             /* name of relation   */
  int m;                   /* num of fields      */
  int i;                   /* the desired record */
{
    Boolean isFirst = True;
    int j;
    char *p = buf;
    
    *buf = '\0';     /* make sure string is terminated here */
    for (j=ATTR_1ST(rel); j<m; j++)
    {
	if (PQgetvalue(portal,i,j) == NULL)
	    continue;
	sprintf(p, "%s%s.%s=\"%s\"", (isFirst ? "" : " and "), class,
		PQfname(portal,i,j), PQgetvalue(portal,i,j));
	isFirst = False;
	p += strlen(p);
    }
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
void checkInputChar(w, type, cbs)
  Widget w;
  int type;
  XmTextVerifyCallbackStruct *cbs;
{
    char *val = XmTextFieldGetString(w);
    char *tmp;
    int  i;

    if (type==16)                 /* boolean */
    {	
	XtRemoveAllCallbacks(w, XmNmodifyVerifyCallback);
	if (cbs->text->length==1 && 
	    (tolower(cbs->text->ptr[0])=='t' 
	     || tolower(cbs->text->ptr[0])=='f'))
	    if (*val == '\0')
	    {
		XtVaSetValues(w, XmNvalue, "''", NULL);
		cbs->doit = False;
		tmp = (char *)malloc(cbs->text->length + 1);
		strncpy(tmp, cbs->text->ptr, cbs->text->length);
		tmp[cbs->text->length] = '\0';
		XmTextFieldInsert(w, 1, tmp);
		free(tmp);
	    }
    }
    if (type>=21 && type<=23)     /* integer value */
	for (i=0; i<cbs->text->length; i++)
	{
	    if (*val=='\0' || !(isdigit(*val) || *val=='-' || *val=='+'))
		continue;   /* if 1st char is non-numeric - allow anything */
/*	    if (*val=='\0' && i==0 && cbs->text->ptr[i]=='-')
		continue;
*/
	    if (!isdigit(cbs->text->ptr[i]))    /* insert everything up to */
	    {                                   /* the 1st non-digit char  */
		cbs->text->length=i;
		break;
	    }
	}
    if (type==19 || type==25)
	if (*val == '\0')
	{
	    XtRemoveAllCallbacks(w, XmNmodifyVerifyCallback);
	    XtVaSetValues(w, XmNvalue, "\"\"", NULL);
	    cbs->doit = False;
	    tmp = (char *)malloc(cbs->text->length + 1);
	    strncpy(tmp, cbs->text->ptr, cbs->text->length);
	    tmp[cbs->text->length] = '\0';
	    XmTextFieldInsert(w, 1, tmp);
	    free(tmp);
/*
	    XtVaSetValues(w, XmNvalue, "\"\"", NULL);
	    cbs->startPos = 1;
	    cbs->endPos += 1;
*/
	}
    XtFree(val);
}





/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/* field checking not implemented yet (maybe never...) */
void checkField(w, clientData, cbs)
  Widget w;
  XtPointer clientData, cbs;
{
    char *value = XmTextFieldGetString(w);

    XtFree(value);
}
