/* das ist ein -*- C -*- Programm */
#include <X11/cursorfont.h>

#ifdef MOTIF11
#include <Xm/Xm.h>
#endif

#ifdef RDD
extern Boolean rddDeliverMessageEvent(
#if NeedFunctionPrototypes
    XEvent
#endif
);
#endif

extern Boolean wafeOwnSelectionCmd(
#if NeedFunctionPrototypes
     String, String, String
#endif
);

extern void callback (
#if NeedFunctionPrototypes
     Widget, char *, XtCallbackProc, char *
#endif
);

extern Cardinal wafeGetResourcesOfClass(
#if NeedFunctionPrototypes
     WidgetClass, QTypeList *
#endif
);

extern Cardinal wafeGetConstraintsOfClass(
#if NeedFunctionPrototypes
     WidgetClass, QTypeList *
#endif
);

extern void wafeCallbackCmd(
#if NeedFunctionPrototypes
     Widget, String, XtCallbackProc, String
#endif
);

#ifdef ATHENA
#include <X11/Xaw/List.h>
#endif


static void
action(w, TranslationModifier, translationTable)
Widget	w;
int	TranslationModifier;
XtTranslations translationTable;
    {
    switch (TranslationModifier) 
        {
	case 1: XtOverrideTranslations(w, translationTable);
		break;
	case 2: XtAugmentTranslations(w, translationTable);
		break;
	case 3: XtVaSetValues (w, XtNtranslations, translationTable, NULL);
		break;
        }
    }


static void
realizeWidgets()
    {
    WidgetListPtr ptr = wafeWidgetTrees;
    while(ptr) 
	{
	XtRealizeWidget(ptr->widget);
	ptr = ptr->next;
	}
    }

static void
callCallbacks(w, callback, argc,argv)
Widget   w;
String   callback;
int      argc;
char   **argv;
    {
    float   f;
    if (argc == 0)
	XtCallCallbacks(w, callback, NULL);
    else
	{
	if (!strcmp(argv[0], "float"))
	    {
	    sscanf(argv[1], "%f", &f);
	    XtCallCallbacks(w, callback, (XtPointer)&f);
	    }
	else
	    wafeWarn("callCallbacks","Wrong type: %s", argv[1], NULL,NULL);
	}
    }


static void
callActionProc(w, eventType, action, argc, argv)
Widget         w;
String         eventType;
String         action;
int            argc;
char         **argv;
    {
    char          *token;
    XEvent         event;
    Display       *display;
    Window         win, root, child;
    int            x, y, xR, yR;
    unsigned int   state, button, keycode;
    char *procName = "callActionProc";

    display = XtDisplay(wafeTopLevel);
    win = XtWindow(w);

    if (!strcmp(eventType, ""))
	{
	event.type = 0;
	event.xany.display = display;
	event.xany.window = win;
	}
    else
	{
	token = strtok(eventType, " ");
	if (strcmp(token, "type"))
	    {
	    wafeWarn(procName,"Missing type; aborting command",
		      NULL,NULL,NULL);
	    return;
	    }

	XQueryPointer(display, win, &root, &child, &xR, &yR, &x, &y, &state);

	token = strtok(NULL, " ");
	if (!strcmp(token, "buttonEvent"))
	    {
	    token = strtok(NULL, " ");
	    if (!strcmp(token, "button"))
		{
		token = strtok(NULL, " ");
		if (!strcmp(token, "button1"))
		    button = Button1;
		else
                if (!strcmp(token, "button2"))
		    button = Button2;
                else
                if (!strcmp(token, "button3"))
		    button = Button3;
		else
                if (!strcmp(token, "button4"))
		    button = Button4;
		else
                if (!strcmp(token, "button5"))
		    button = Button5;
		else
		    {
		    wafeWarn(procName,"Unknown Button, %s", 
			     token,NULL,NULL);
		    return;
		    }
		}
	    else
		{
		wafeWarn(procName,
			 "This event field (%s) can't be specified\n", 
			 token,NULL,NULL);
		return;
		}

	    event.type = ButtonPress;
	    event.xbutton.display = display;
	    event.xbutton.window = win;
	    event.xbutton.root = root;
	    event.xbutton.subwindow = child;
	    event.xbutton.x = x;
	    event.xbutton.y = y;
	    event.xbutton.x_root = xR;
	    event.xbutton.y_root = yR;
	    event.xbutton.state = state;
	    event.xbutton.button = button;
	    }
	else if(!strcmp(token, "keyEvent"))
	    {
	    token = strtok(NULL, " ");
	    if (!strcmp(token, "key"))
		{                   
		token = strtok(NULL, " ");
		keycode = XKeysymToKeycode(display, XStringToKeysym(token));
		}
	    else
		{
		wafeWarn(procName,
			 "This event field (%s) can't be specified", 
			 token,NULL,NULL);
		return;
		}

	    event.type = KeyPress;
	    event.xkey.display = display;
	    event.xkey.window = win;
	    event.xkey.root = root;
	    event.xkey.subwindow = child;
	    event.xkey.x = x;
	    event.xkey.y = y;
	    event.xkey.x_root = xR;
	    event.xkey.y_root = yR;
	    event.xkey.state = state;
	    event.xkey.keycode = keycode;
	    }
	else
	    {
	    wafeWarn(procName,
		     "This event field (%s) can't be specified", 
		     token,NULL,NULL);
	    return;
	    }                    
	}

    XtCallActionProc(w, action, &event, argv, argc);
    return;
    }

/* used by addTimeOut */
static void
timerProc(command, junk)
XtPointer        command;
XtIntervalId     *junk;
    {
    DBUG_ENTER("timerProc");
    (void) wafeEval(wafeInterpreter, (char *)command, "timerProc");
    XtFree((char *)command);
    DBUG_VOID_RETURN;
    }

/* used by addTimeOut */
static Boolean
workProc(command)
XtPointer command;
    {
    char *r;
    int  rc;

    DBUG_ENTER("workProc");
    rc = wafeEval(wafeInterpreter, (char *)command, "workProc");
    r = wafeInterpreter->result;
    if ( rc || ( r && *r++ == '1' && *r == '\0' ) ) /* don't do it again */
	{
	XtFree((char *)command);
	DBUG_RETURN(True);
	}
    DBUG_RETURN(False);
    }


static char *
cvtToString(clientData, quarkOfType, freeProcPtr)
XtPointer     clientData;
XrmQuark      quarkOfType;
freeProc      *freeProcPtr;
    {
    XrmValue   input,output;
    
    DBUG_PRINT("cvtToString", ("Converting to <%s>", 
			       XrmQuarkToString(quarkOfType)));

    input.addr  =  clientData;            /* ??? */
    input.size  =  sizeof(XtPointer);     /* ??? */
    output.addr =  NULL;

    IFCONVERTANDSTORE(wafeTopLevel, 
		      XrmQuarkToString(quarkOfType),input, 
		      XtRString, output)
	{
	*freeProcPtr = XtFree;
	return output.addr;
	}
    else
	return NULL;
    }



static String
getValue(w, Qname, freeProcPtr)
Widget        w;
XrmQuark      Qname;
freeProc     *freeProcPtr;
    {
    char      *result;
    XrmQuark   Qtype;
    XtPointer  value;
    Arg        arg;
    char *procName = "getValue";

    DBUG_PRINT(procName, ("widget is %p resource is <%s>",
			    w,XrmQuarkToString(Qname))); 

    if (!(Qtype = wafeGetQTypeOfAttribute(XtClass(w), ParentWidget(w), Qname)))
	{
	wafeWarn(procName,"Unknown Attribute %s",
		 XrmQuarkToString(Qname),NULL,NULL);
	*freeProcPtr = TCL_STATIC;
	return NULL;
	}

    if (Qtype == qString)
	{
	String    valueString;
	*freeProcPtr = TCL_VOLATILE;
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueString, NULL);
	return valueString;
	}

    /* this is the Default value for the following types 
     * assuming that dense code is better than one instruction more
     */ 
    *freeProcPtr = XtFree;
	
    if (Qtype == qInt) 
	{
	int valueInt;
	result = XtMalloc(INT_AS_STRING);
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueInt, NULL);
	sprintf(result, "%d", (int)valueInt);
	return result;
	}

    if (Qtype == qPosition
#ifdef MOTIF11
	|| Qtype == qVerticalPosition
	|| Qtype == qHorizontalPosition
#endif
       )
	{
	Position    valuePosition;
	result = XtMalloc(INT_AS_STRING);
	XtVaGetValues(w, XrmQuarkToString(Qname), &valuePosition, NULL);
	sprintf(result, "%hd", valuePosition);
	return result;
        }
#ifdef MOTIF11
    if (Qtype == qTextPosition) 
	{
	XmTextPosition    valuePosition;
	result = XtMalloc(INT_AS_STRING);
	XtVaGetValues(w, XrmQuarkToString(Qname), &valuePosition, NULL);
	sprintf(result, "%ld", valuePosition);
	return result;
        }
#endif

    if (Qtype == qDimension
#ifdef MOTIF11
	|| Qtype == qVerticalDimension
	|| Qtype == qHorizontalDimension
#endif
	) 
	{
	Dimension    valueDimension;
	result = XtMalloc(INT_AS_STRING);
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueDimension, NULL);
	sprintf(result, "%hu", valueDimension);
	return result;
        }

    if (Qtype == qCardinal)
	{
	Cardinal    valueCardinal;
	result = XtMalloc(INT_AS_STRING);
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueCardinal, NULL);
	sprintf(result, "%u", valueCardinal);	
	return result;
	}

    if (Qtype == qBoolean)
	{
	Boolean    valueBoolean;
	*freeProcPtr = TCL_STATIC;
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueBoolean, NULL);
	return valueBoolean ? "true" : "false";   
	}


    if (Qtype == qFloat) 
	{
	float    valueFloat;
	result = XtMalloc(FLOAT_AS_STRING);
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueFloat, NULL);
	sprintf(result, "%f", valueFloat);
	return result;
	}

    if (Qtype == qDouble) 
	{
	double    valueDouble;
	result = XtMalloc(FLOAT_AS_STRING);
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueDouble, NULL);
	sprintf(result, "%f", valueDouble);
	return result;
	}

    if (Qtype == qPixel) 
	{
	XColor  c;
	Display *dpy = XtDisplay(w);
	result = XtMalloc(8); /* "#RRGGBB" */
	XtVaGetValues(w, XrmQuarkToString(Qname), &(c.pixel), NULL);
	XQueryColor(dpy,DefaultColormap(dpy, DefaultScreen(dpy)),&c);
	sprintf(result, "#%02X%02X%02X", 
		(int)(c.red>>8), (int)(c.green>>8), (int)(c.blue>>8));
	return result;
	}

     /* Callbacks sind ... anders: die String-Information 
	kommt aus wafe's privatem Memory-Management und nicht 
	über getValues!
      */
    if (Qtype == qCallback) 
	{
	MMattribListPtr ptr = *wafeMMgetAttribList(w,True);
	
	*freeProcPtr = TCL_STATIC;
	result = "";
	while(ptr) 
	    {
	    if (ptr->attribute == Qname) 
		{
		result = ((char*)ptr->value) +
		    sizeof(XtCallbackList)*2 +
		    sizeof(XtCallbackRec[2]) +
		    sizeof(XrmQuark);
		break;
		}
	    ptr = ptr->next;
	    }
	return result;
	}

    if (Qtype == qWidget
#ifdef MOTIF11
	|| Qtype == qWindow
#endif
	)
	{
	Widget valueWidget;
	*freeProcPtr = TCL_STATIC;
	XtVaGetValues(w, XrmQuarkToString(Qname), &valueWidget, NULL);
	return widget2Name(valueWidget);
	}

    if (Qtype == qWidgetList) 
        {
	WidgetList children;
        Cardinal   numChildren;
        XtVaGetValues(w, XtNnumChildren, &numChildren,
                         XtNchildren,    &children,    NULL);
	return wafeCvtWidgetListToList(NULL, numChildren, children);
        }

#ifdef ATHENA
    if ((Qtype == qPointer) && (XtClass(w) == listWidgetClass))
        {
	char **argv;
	int argc;

        XtVaGetValues(w, XtNnumberStrings, &argc,
                         XtNlist,          &argv,    NULL);
	return Tcl_Merge(argc, argv);
        }
#endif

    else 
	{
	  /*
	   * warning: cvtToString assumes, you can free the result 
	   * using XtFree
	   */
	XtSetArg(arg, XrmQuarkToString(Qname), &value);
	XtGetValues(w, &arg, (Cardinal)1);

	if (!(result = cvtToString(value, Qtype, freeProcPtr)))
	    {
	    wafeWarn(procName, "Couldn't convert value for %s to string",
		     XrmQuarkToString(Qname),NULL,NULL);
	    return NULL;
	    }
	return result;
      }
    }

static int
getResourceList(w, resultPtr)
char    ***resultPtr; 
Widget   w;
    {
    int numTotal, count;
    char **result;
    int rNum = 0;
    int cNum = 0;
    QTypeList rList, cList;

    rNum = wafeGetResourcesOfClass(XtClass(w), &rList);

    if (XtIsConstraint(ParentWidget(w)))   /* If parentWidget is constrain widget => fetch constraints */
	cNum =  wafeGetConstraintsOfClass(XtClass(ParentWidget(w)), &cList);

    numTotal = rNum + cNum;

    *resultPtr = result = (char **)XtMalloc(numTotal *sizeof(char *));       

    for ( count = 0; count < rNum; count++)
	*result++ = XrmQuarkToString(rList[count].qName);

    for ( count = 0; count < cNum; count++)
	*result++ = XrmQuarkToString(cList[count].qName);

    return(numTotal);
    }



typedef struct _busyStruct
    {
    Window                busyWindow;
    Boolean               state;
    } busyStruct, *busyStructPtr;


static void
setBusy(w,sensitive)
Widget w;
Boolean sensitive;
    {
    unsigned long valuemask;
    XSetWindowAttributes attributes;
    MMattribListPtr *al = wafeMMgetAttribList(w,True);
    busyStructPtr bs = (busyStructPtr) wafeMMgetValue(al, qbusyWindow);
    Display *dpy = XtDisplay(w);

    if (sensitive)
	{
	if (!bs)
	    {
	    bs = (busyStructPtr) XtMalloc(sizeof(busyStruct));
	    wafeMMreplace(al,qbusyWindow,(char*)bs,XtFree);
            bs->state = False;

	    /* Ignore device events while the busy cursor is displayed. */
	    valuemask = CWDontPropagate | CWCursor;
	    attributes.do_not_propagate_mask =  (KeyPressMask | KeyReleaseMask |
						 ButtonPressMask | ButtonReleaseMask | 
						 PointerMotionMask);
	    attributes.cursor = XCreateFontCursor(dpy, XC_watch);

	  /* The window will be as big as the display screen, and clipped by
	   * its own parent window, so we never have to worry about resizing
	   */

	    bs->busyWindow = XCreateWindow(dpy, XtWindow(w), 0, 0,
					   HeightOfScreen(XtScreen(w))*2, 
					   WidthOfScreen(XtScreen(w))*2, 
					   (unsigned int) 0, 0, InputOnly,
					   CopyFromParent, valuemask, 
					   &attributes);
	    }
	if (!bs->state) 
	     {
	     XMapRaised(dpy, bs->busyWindow);
	     bs->state = True;
	     }
        }
    else
	{
	if (bs && bs->state)
	    {
	    XUnmapWindow(dpy, bs->busyWindow);
	    bs->state = False;
	    }
	}
    }


static String
waitForVariable(varName)
String varName;
    {
    String result = NULL;
    Tcl_UnsetVar(wafeInterpreter,varName,TCL_GLOBAL_ONLY);
    wafeInnerEventLoop ++;
    while (!(result = Tcl_GetVar(wafeInterpreter,varName,TCL_GLOBAL_ONLY)))
	XtAppProcessEvent(wafeAppContext, XtIMAll);
    wafeInnerEventLoop --;
    return result;
    }

typedef struct {
    String  result;
  } ApplData, *ApplDataPtr;

static ApplData   applData;
static XtResource applResources[] = 
    {
	{
	NULL,                            /* resource_name   */
	NULL,                            /* resource_class  */
	XtRString,                       /* resource_type   */
	sizeof(String),                  /* resource_size   */
	XtOffset(ApplDataPtr, result),   /* resource_offset */
	XtRImmediate,                    /* default_type    */
	"",                              /* default_addr    */
	}
     };


static String
getApplicationResource(w, nameString, classString)
Widget w;
String nameString;
String classString;
    {
    XtResource *res = (XtResource *)XtMalloc(sizeof(XtResource));
    memcpy(res,applResources,sizeof(XtResource));
       
    res->resource_name  = nameString;
    res->resource_class = classString;
    XtVaGetApplicationResources(w, &applData, res,
				XtNumber(applResources), NULL);
    XtFree((char *) res);
     
    return applData.result;
    }


static void
processPendingEvents()
    {
    while (XtAppPending(wafeAppContext))
       {
       XEvent event;
       XtAppNextEvent(wafeAppContext,&event);
#ifdef RDD
       if (rddDeliverMessageEvent(event))
	   continue;
#endif
       wafeInnerEventLoop ++;
       XtDispatchEvent(&event);
       wafeInnerEventLoop --;
       }
    }

typedef int MapState;

static MapState
mapState(w)
Widget w;
    {
    XWindowAttributes wattr;
    Window win = XtWindow(w);
    
    if (!win) return 0;
    if (!XGetWindowAttributes(XtDisplay(w), win, &wattr)) return 0;
    return wattr.map_state;
    }


static Status
combineFileDatabase(fname,w,override)
String fname;
Widget w;
Bool override;
    {
    XrmDatabase dispDb = XtDatabase(XtDisplay(w)); 
    XrmCombineFileDatabase(fname, &dispDb, override);
    }


static Boolean
motifWMRunning(shell)
Widget shell;
    {
#ifdef MOTIF
    return XmIsMotifWMRunning(shell);
#else
    Display  *dpy         = XtDisplay(shell);
    Window    root        = RootWindowOfScreen(XtScreen(shell));
    Atom      motifWmInfo = XInternAtom(dpy, "_MOTIF_WM_INFO", False);

    Atom      actual_type;
    int       actual_format;
    unsigned long nitems, bytes_after;
    unsigned char* prop;

    XGetWindowProperty(dpy, root, motifWmInfo, 0,0, FALSE,motifWmInfo,
		       &actual_type,&actual_format,&nitems,&bytes_after,
		       &prop);
    return (actual_type == motifWmInfo && actual_format == 32);
#endif
    }
