/* yes buddy, it is a -*- c -*- program! */

 /*
    * This file contains a new converter procedure and xpm support procedures:
    *
    *    A new StringToBitmap Converter, which is also able to convert
    *    Pixmaps (suffix .xpm). First it searches for a valid BitmapFile
    *    under the given filename; if the file format is invalid, it
    *    tries to interpret it as a pixmap file.
    */ 


#ifdef XPM
#include <xpm.h>
#include <X11/extensions/shape.h>
#endif
#include <X11/Xos.h>
#include <X11/Xmu/Xmu.h>

#ifdef XPM
 /* Create a linked list, which keeps track of all created pixmaps,
  * so that we can destroy Colorcells, if they are not longer needed.
  * Only the changePixmap command will use this feature, so pixmaps which
  * are set with setValues are still cached as usual!
  * Note that setting a pixmap with setValues therefore implies loosing these 
  * colorcells for the application's lifetime!
  */

typedef struct _PixmapInfo
     {
     Pixmap                pm;              /* Pixmap              */
     Widget                widget;          /* Widget              */
     XpmAttributes         *attribPtr;      /* Colorcells, etc     */
     char                 *pmName;          /* The filename..      */
     struct _PixmapInfo   *next;
     }  PIXMAPINFO, *PIXMAPINFOPTR;

PIXMAPINFOPTR  pixmapListHead = NULL;
#endif

/* Additional Args for the converter: 
 */

static XtConvertArgRec xpmConvertArgsPixmap[] = {
  {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.window), 
   sizeof(Window)},
  {XtImmediate, (XtPointer)True, sizeof(Boolean)}
};


static XtConvertArgRec xpmConvertArgsBitmap[] = {
  {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.window), 
   sizeof(Window)}
};
   

#ifdef XPM
void
changePixmap(w,resource,fname)
Widget     w;
String     resource;
String     fname;
    {
    Colormap        cmap;
    Pixmap          death, pixmap, mask;
    Screen         *screen;
    Display        *dpy;
    int             screenNum;
    int             returnVal;
    unsigned int    depth;
    XpmAttributes  *attribPtr;
    PIXMAPINFOPTR   pptr, cptr;
    
    dpy = XtDisplay(w);
    screen = DefaultScreenOfDisplay(dpy);
    screenNum = DefaultScreen(dpy);
    depth = DefaultDepth(dpy, DefaultScreen(dpy));

    /* Get pixmap adress and colormap of the pixmap, which will be destroyed  */
    
    XtVaGetValues(w, resource,     &death, 
		  XtNcolormap, &cmap, NULL);

  /* Search the list of pixmaps to find out wether the specified pixmap's 
      * resources are to be freed - which is the case when it was created by
      * this procedure.
      */

    if (pixmapListHead)
	{
	for (pptr = pixmapListHead; pptr != NULL; pptr = pptr->next)
	    {
	    if ((pptr->pm == death) && (pptr->widget == w))
		{
		if (!strcmp(pptr->pmName, fname))
		    {
		    DBUG_PRINT("pixmap",
			       ("You are about to set the same pixmap!"));
		    return;
		    }

		/* Set pixmap to "None" => destroying will be save */
		XtVaSetValues(w, resource, None, NULL);

		/* Take destroyed Pixmap from pixmaplist and deallocate memory */
		DBUG_PRINT("pixmap",
			   ("found pixmap %s of widget %s, %d colors freed", 
			    resource, XtName(w), pptr->attribPtr->npixels));

		XFreePixmap(XtDisplay(w), death);
		if (pptr->attribPtr->depth > 1) 
		    XFreeColors(XtDisplay(w), cmap, pptr->attribPtr->pixels, 
				pptr->attribPtr->npixels, 0);

		XpmFreeAttributes(pptr->attribPtr);

		DBUG_PRINT("pixmap", ("Managing the List"));
		if (pptr == pixmapListHead)
		    {
		    XtFree((char *)pixmapListHead);
		    pixmapListHead = NULL;
		    }
		else
		    {
		    for(cptr = pixmapListHead; 
			cptr->next != pptr; 
			cptr = cptr->next);
		    
		    if (pptr->next == NULL)     /* last Pixmap in list */
			cptr->next = NULL;
		    else                        /* not last Pixmap in list */
			cptr->next = pptr->next;

		    XtFree((char *)pptr);
		    }
		}
	    }
	}

    /* If the new pixmap's filename is None, => don't create new entry. */

    if (!strcmp(fname, "None"))
	{
	DBUG_PRINT("pixmap",
		   ("Setting %s %s to None", XtName(w), resource));
	XtVaSetValues(w, resource, None, NULL);
	return;
	}
    if (!strcmp(fname, "Unspecified"))
	{
	DBUG_PRINT("pixmap",
		   ("Setting %s %s to Unspecified", XtName(w), resource));
	XtVaSetValues(w, resource, XtUnspecifiedPixmap, NULL);
	return;
	}
    if (!strcmp(fname, "ParentRelative"))
	{
	DBUG_PRINT("pixmap",
		   ("Setting %s %s to ParentRelative", XtName(w), resource));
	XtVaSetValues(w, resource, ParentRelative, NULL);
	return;
	}

    /* Now convert the new Pixmap-File, create a new element of the list 
         * and fill it with information.
         */

    attribPtr = (XpmAttributes *)XtCalloc(sizeof(XpmAttributes), sizeof(char));
    attribPtr->valuemask = XpmReturnPixels | XpmDepth | XpmReturnInfos;
    attribPtr->depth = DefaultDepthOfScreen(DefaultScreenOfDisplay(XtDisplay(w)));
    /* fprintf(stderr, "Wafe(changePixmap): calling xpmreadfile\n");*/
    returnVal = XpmReadFileToPixmap(dpy, RootWindow(dpy, screenNum), 
				    fname, &pixmap, &mask, attribPtr); 
    /*fprintf(stderr, "Wafe(changePixmap): xpmreadfile returned %d\n",returnVal);*/

    if (returnVal == XpmSuccess)
	{
	if (!pixmapListHead)   /* First Pixmap */
	    {
	    cptr = 
		pixmapListHead = 
		    (PIXMAPINFOPTR)XtMalloc(sizeof(PIXMAPINFO));
	    pixmapListHead->next = NULL;
	    }
	else
	    {
	    for(cptr = pixmapListHead; cptr->next != NULL; cptr = cptr->next);
	    cptr->next = (PIXMAPINFOPTR)XtMalloc(sizeof(PIXMAPINFO));
	    cptr = cptr->next;
	    cptr->next = NULL;
	    }

	cptr->widget = w;
	cptr->pm = pixmap;
	cptr->pmName = XtNewString(fname);
	cptr->attribPtr = attribPtr;

	DBUG_PRINT("pixmap", 
		   ("%d colorcells are used for %s\n", 
		    cptr->attribPtr->npixels, cptr->pmName));
     
	XtVaSetValues(w, resource, pixmap, NULL);

/*   fprintf(stderr, "changePixmap's window is %p\n", XtWindow(w));*/
/* do we really want to shape the pixmap? */

	if XtWindow(w) 
	      XShapeCombineMask(XtDisplay(w), XtWindow(w), ShapeBounding,
		      0, 0, mask, ShapeSet);

	DBUG_PRINT("pixmap", ("Conversion OK, new Pixmap was set"));
	return;
	}
    else
	{
	fprintf(stderr, "Wafe(changePixmap): Couldn't convert\n");
	XpmFreeAttributes(attribPtr);
	return;
	}
    }     

void
setIconPixmap(w,fname)
Widget   w;
String   fname;
    {
    Window        iconWindow;
    Pixmap        pixmap, mask;
    int           iconWidth, iconHeight;
    XpmAttributes attrib;
    Window        root;
    Display      *dpy;
    Pixel         bpix;

    dpy = XtDisplay(w);
    root = RootWindow(dpy, DefaultScreen(dpy));
    bpix = XBlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
    attrib.valuemask = XpmReturnInfos;

    if (XpmReadFileToPixmap(dpy, root, fname, &pixmap, &mask, &attrib) 
	!= XpmSuccess)
	{
	fprintf(stderr, "Wafe(setIconPixmap): couldn't convert %s to pixmap\n",
		fname);
	return;
	}
    
    iconWidth = attrib.width;
    iconHeight = attrib.height;
    iconWindow = XCreateSimpleWindow(dpy, root, 0, 0, 
				     iconWidth, iconHeight, 1, bpix, bpix);

    XSetWindowBackgroundPixmap(dpy, iconWindow, pixmap);
    XShapeCombineMask(dpy, iconWindow, ShapeBounding,
			  0, 0, mask, ShapeSet);
    
    XtVaSetValues(w, XtNiconWindow, iconWindow, NULL);
    return;
    }
#endif /* XPM */


#define done(address, type) \
        { (*toVal).size = sizeof(type); (*toVal).addr = (caddr_t) address; }
#define	newDone(type, value) \
	{							\
	    if (toVal->addr != NULL) {				\
		if (toVal->size < sizeof(type)) {		\
		    toVal->size = sizeof(type);			\
		    return False;				\
		}						\
		*(type*)(toVal->addr) = (value);		\
	    }							\
	    else {						\
		static type static_val;				\
		static_val = (value);				\
		toVal->addr = (XtPointer)&static_val;		\
	    }							\
	    toVal->size = sizeof(type);				\
	    return True;					\
	}
               

Boolean
CvtStringToPixmapOrBitmap(dpy, args, num_args, fromVal, toVal,
		   converter_data)
Display *dpy;
XrmValuePtr args;
Cardinal *num_args;
XrmValuePtr fromVal;
XrmValuePtr toVal;
XtPointer *converter_data;
    {
    static Pixmap    pixmap;               /* static for cvt magic */
    String           name;
    Screen          *screen;
    int              screenNum;
    String           fn = NULL;
    unsigned int     width, height;
    int              xhot, yhot;
    unsigned char   *data;
    int              returnValue;
    Boolean          toPixmap = *num_args>1;
#ifdef XPM
    Pixmap           mask;
    Window           coreWin;
    XpmAttributes    attrib;
#endif

    pixmap = (Pixmap) NULL;
    name   = (String) fromVal->addr;
/*
    fprintf(stderr,"i am converting <%s> to %s\n",   
	  name, toPixmap ? "Pixmap" : "Bitmap");
 */
    screen = DefaultScreenOfDisplay(dpy);
    screenNum = DefaultScreen(dpy);
    
    if (strcmp(name, "None") == 0) 
        {
        pixmap = None;
        newDone(Pixmap,pixmap);
        }

    if (strcmp(name, "ParentRelative") == 0 && toPixmap) 
        {
        pixmap = ParentRelative;
        newDone(Pixmap,pixmap);
        }

    if (strcmp(name, "Unspecified") == 0 && toPixmap) 
        {
        pixmap = XtUnspecifiedPixmap;
        newDone(Pixmap,pixmap);
        }

    /* try hard to locate bitmap or pixmap file:
       is it the name a sufficient path?
     */
    if (access(name, R_OK) == 0)
	fn = name;
    else
	{
	if (!(fn = XtResolvePathname(dpy, "bitmaps", name, "", 
			       wafeFileSearchPath, NULL, 0, NULL)))
	    {
	    XtStringConversionWarning(name, toPixmap ? "Pixmap" : "Bitmap");
	    fprintf(stderr, 
		    "Wafe(convert): Wafe's current FILESEARCHPATH is <%s>\n", 
		    wafeFileSearchPath);  
	    return False;
	    }
	}
    
    /* we have a valid filename fn */
    /* try to read file as bitmap file... */

	if (XmuReadBitmapDataFromFile(fn, &width, &height, &data,
				      &xhot, &yhot) == BitmapSuccess)
	    {
	    pixmap = XCreatePixmapFromBitmapData(dpy,
				 RootWindowOfScreen(screen),
				 (char *) data, width, height, 1, 0, 
				 toPixmap?DefaultDepth(dpy, DefaultScreen(dpy)):1);
            /* in 0.95 we had:   DefaultDepth(dpy, DefaultScreen(dpy)));*/

	    XFree((char *)data);

	    if (name != fn) XtFree(fn);
	    newDone(Pixmap,pixmap);
	    }

#ifdef XPM    
    attrib.valuemask = XpmReturnPixels | XpmDepth | XpmReturnInfos;
    attrib.depth = DefaultDepthOfScreen(DefaultScreenOfDisplay(dpy));

    if ((returnValue = XpmReadFileToPixmap(dpy,RootWindow(dpy,screenNum),
					   fn, &pixmap, &mask, &attrib))
	!= XpmSuccess)
	{
	fprintf(stderr, "Wafe(convertPixmap): XPM-Error-Code: %d\n", 
		returnValue);
#else
	fprintf(stderr, "Wafe(convertPixmap): No XPM configured!\n");
#endif
	XtStringConversionWarning(name, toPixmap ? "Pixmap" : "Bitmap");

	if (name != fn) XtFree(fn);
	return False;
#ifdef XPM
	}
    else 
	{
/*
	fprintf(stderr,"pixmap=%p, width=%d, height=%d, addr=%p\n",pixmap,
		attrib.width,attrib.height,toVal->addr);
*/
	coreWin = *((Window *) args[0].addr);
/*	fprintf(stderr, "The window is %p\n", coreWin); */
	
	/* if toVal->addr is set we assume that the value comes
           from a resource file, in which case we can't assume that
           the core fields of the widget are already initialized; 
           coreWin will be wrong; Wafe never sets toVal->addr */
	if (coreWin && !toVal->addr) 
	    XShapeCombineMask(dpy, coreWin, ShapeBounding,
			      0, 0, mask, ShapeSet);

	if (name != fn) XtFree(fn);
	newDone(Pixmap,pixmap);
	}
#endif /* XPM */
    }

/* to keep knowledge about xpmConvertArgsPixmap etc local ...
 */
void
xpmRegisterTypeConverter(resourceType, toPixmap)
char * resourceType;
Boolean toPixmap;
    { 
    if (toPixmap)
	XtSetTypeConverter(XtRString, 
			   resourceType, 
			   (XtTypeConverter)CvtStringToPixmapOrBitmap, 
			   xpmConvertArgsPixmap, 
			   2,
			   XtCacheByDisplay,
			   NULL);
    else
	XtSetTypeConverter(XtRString, 
			   resourceType, 
			   (XtTypeConverter)CvtStringToPixmapOrBitmap, 
			   xpmConvertArgsBitmap, 
			   1, 
			   XtCacheByDisplay, 
			   NULL);
    }

