/* 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                */
     Pixmap                mask;            /* shape mask            */
     Widget                widget;          /* Widget                */
     XpmAttributes         *attribPtr;      /* Colorcells, etc       */
     char                 *pmName;          /* The filename..        */
     int                   count;           /* references            */
     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)}
};
   

/* try hard to locate bitmap or pixmap file */
char *
wafeLocateBitmap(dpy, name, toPixmap)
Display *dpy;
char * name;
Boolean toPixmap;
    {
    char *fn;

    /* is it the name a sufficient path? */
    if (access(name, R_OK) == 0)
	return name;
    else
	{
	if (!(fn = XtResolvePathname(dpy, "bitmaps", name, "", 
			       wafeFileSearchPath, NULL, 0, NULL)))
	    {
	    XtStringConversionWarning(name, toPixmap ? "Pixmap" : "Bitmap");
	    wafeWarn("convert", "Wafe's current FILESEARCHPATH is <%s>", 
		     wafeFileSearchPath,NULL,NULL);  
	    }
	return fn;
	}
    }


#ifdef XPM
static void
changePixmap(w,resource,fname)
Widget     w;
String     resource;
String     fname;
    {
    Colormap        cmap;
    Pixmap          death, pixmap, mask;
    int             returnVal;

    XpmAttributes  *attribPtr;
    PIXMAPINFOPTR   pptr, cptr;
    char           *fn;

    Display        *dpy       = XtDisplay(w);
    Window          win       = XtWindow(w);
    int             screenNum = DefaultScreen(dpy);
    unsigned int    depth     = DefaultDepth(dpy, screenNum);

    /* 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 whether 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)
		{
		if (pptr->widget == w && !strcmp(pptr->pmName, fname))
		    {
		    DBUG_PRINT("pixmap",
			       ("You are about to set the same pixmap!"));
		    return;
		    }

                pptr->count--;
                if (pptr->count < 1) 
		    {
		    /* destroy Pixmap and deallocate resources */
		    DBUG_PRINT("pixmap",
			    ("deallocating pixmap %s, %d colors freed", 
			     pptr->pmName, pptr->attribPtr->npixels));

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

		    if (pptr->attribPtr->depth > 1) 
		        XFreeColors(dpy, cmap, 
				    pptr->attribPtr->pixels, 
				    pptr->attribPtr->npixels, 0);
		    if (pptr->mask) 
		        XFreePixmap(dpy,pptr->mask);

		    XpmFreeAttributes(pptr->attribPtr);

		    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);
		        }
		    }
		}
	    }

	/* is the pixmap already in the cache? */
	for (pptr = pixmapListHead; pptr != NULL; pptr = pptr->next)
	  {
	  if (!strcmp(pptr->pmName, fname))
	       {
	       DBUG_PRINT("pixmap", ("Pixmap %s found in cache",fname));
	       pptr->count ++;

	       XtVaSetValues(w, resource, pptr->pm, NULL);
	       if (win)
	          XShapeCombineMask(dpy, win, ShapeBounding,
				    0, 0, pptr->mask, ShapeSet);

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

    if (!strcmp(fname, "None"))
	{
	DBUG_PRINT("pixmap",
		   ("Setting %s of %s to None", resource, XtName(w)));
	XtVaSetValues(w, resource, None, NULL);
	if (win) 
	  XShapeCombineMask(dpy,win,ShapeBounding,0,0,(Pixmap)NULL,ShapeSet);
	return;
	}
    if (!strcmp(fname, "Unspecified"))
	{
	DBUG_PRINT("pixmap",
		   ("Setting %s %s to Unspecified", XtName(w), resource));
	XtVaSetValues(w, resource, XtUnspecifiedPixmap, NULL);
	if (win) 
	  XShapeCombineMask(dpy,win,ShapeBounding,0,0,(Pixmap)NULL,ShapeSet);
	return;
	}
    if (!strcmp(fname, "ParentRelative"))
	{
	DBUG_PRINT("pixmap",
		   ("Setting %s %s to ParentRelative", XtName(w), resource));
	XtVaSetValues(w, resource, ParentRelative, NULL);
	if (win) 
	  XShapeCombineMask(dpy,win,ShapeBounding,0,0,(Pixmap)NULL,ShapeSet);
	return;
	}


    if (!(fn = wafeLocateBitmap(dpy, fname, True)))
	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 = depth;
    /* fprintf(stderr, "Wafe(changePixmap): calling xpmreadfile\n");*/
    returnVal = XpmReadFileToPixmap(dpy, RootWindow(dpy, screenNum), 
				    fn, &pixmap, &mask, attribPtr); 
    /*fprintf(stderr, "Wafe(changePixmap): xpmreadfile returned %d\n",returnVal);*/
    if (fn != fname) 
	XtFree(fn);

    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->mask = mask;
	cptr->count = 1;
	cptr->pmName = XtNewString(fname);
	cptr->attribPtr = attribPtr;

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

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

	if (win)
	      XShapeCombineMask(dpy, win, ShapeBounding,
		      0, 0, mask, ShapeSet);

	DBUG_PRINT("pixmap", ("New Pixmap was created"));
	return;
	}
    else
	{
	wafeWarn("changePixmap","Couldn't convert %s to pixmap", 
		 fname,NULL,NULL);
	XpmFreeAttributes(attribPtr);
	return;
	}
    }     

static void
setIconPixmap(w,fname)
Widget   w;
String   fname;
    {
    Window        iconWindow = (Window)NULL;
    Pixmap        pixmap, mask;
    int           xpmReturn;
    XpmAttributes attrib;
    Display      *dpy   = XtDisplay(w);
    Window        root  = RootWindow(dpy, DefaultScreen(dpy));
    Pixel         bpix  = XBlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
    String        fn;
    int           x,y;

    if (!(fn = wafeLocateBitmap(dpy, fname, True)))
	return;

    attrib.valuemask = XpmReturnInfos;
    xpmReturn = XpmReadFileToPixmap(dpy, root, fn, &pixmap, &mask, &attrib);
    if (fn != fname) XtFree(fn);
    if (xpmReturn != XpmSuccess)
	{
	wafeWarn("setIconPixmap", "Couldn't convert %s to pixmap",
		 fname,NULL,NULL);
	return;
	}

    XtVaGetValues(w, XtNiconWindow, &iconWindow, NULL);

    if (!iconWindow)
	{
	iconWindow = XCreateSimpleWindow(dpy, root, 0, 0, 
					 attrib.width, attrib.height, 1, 
					 bpix, bpix);
	XtVaSetValues(w, XtNiconWindow, iconWindow, NULL);
	} 
    else 
	{
	XWindowChanges values =
	    {
	    0,0,attrib.width, attrib.height,(Window)NULL,0
	    };

/* reconfigure uglifies icon under MWM */

	if (motifWMRunning(w))
	    {
	    int x,y;
	    unsigned int w,h,b,d;
	    Window r;
	    XIconSize *size_list;
	    int count = 0;
	    
	    XGetGeometry(dpy,iconWindow,&r,&x,&y,&w,&h,&b,&d);
	    XGetIconSizes(dpy, r, &size_list, &count);

	    if (count>0) 
		{
		if (values.width > size_list->max_width)
		    values.width = size_list->max_width;
		if (values.height > size_list->max_height)
		    values.height = size_list->max_height;

		values.x = (size_list->max_width  - values.width ) / 2 + 6;
		values.y = (size_list->max_height - values.height) / 2 + 6;

		XFree((char *)size_list);
		XReconfigureWMWindow(dpy,iconWindow, DefaultScreen(dpy),
				     CWX|CWY|CWWidth|CWHeight, &values);
		}
	    }
	else
	    XReconfigureWMWindow(dpy,iconWindow, DefaultScreen(dpy),
				 CWWidth|CWHeight, &values);
	}

    XSetWindowBackgroundPixmap(dpy, iconWindow, pixmap);
    XShapeCombineMask(dpy, iconWindow, ShapeBounding, 0, 0, mask, ShapeSet);
    XClearWindow(dpy,iconWindow);
    XFreePixmap(dpy,pixmap);
    return;
    }
#endif /* XPM */

#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);
        }

    if (!(fn = wafeLocateBitmap(dpy, name,toPixmap)))
	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)
	{
	char buffer[INT_AS_STRING];
	sprintf(buffer,"%d",returnValue);
	wafeWarn("convertPixmap", "XPM-Error-Code: %d", 
		buffer,NULL,NULL);
#else
	wafeWarn("convertPixmap", "No XPM configured", NULL,NULL,NULL);
#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 (wafeCurrentAttrib =! qiconPixmap)
	    {
	    /* 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);
	    }
	else 
	if (wafeCurrentWidget)
	    XtVaSetValues(wafeCurrentWidget,XtNiconMask,mask,NULL);
	
	if (name != fn) XtFree(fn);
	newDone(Pixmap,pixmap);
	}
#endif /* XPM */
    }

/* to keep knowledge about xpmConvertArgsPixmap etc local ...
 */
void
wafeRegisterXpmTypeConverter(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);
    }

