/**
 *
 * $Id: ImageCache.c,v 1.2 1997/03/16 02:50:26 miers Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static char rcsid[] = "$Id: ImageCache.c,v 1.2 1997/03/16 02:50:26 miers Exp $";

/*
 * --aldi 97/01/11: changed to use the generic hashtable module.
 */
#include <LTconfig.h>
#include <XmI/XmI.h>
#include <XmI/HashI.h>
#include <X11/Xfuncs.h>
#include <XmI/ImageCacheI.h>

#include <Xm/XmP.h>
#include <Xm/ScreenP.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <XmI/DebugUtil.h>

/*
 * The actual caches for images and for pixmaps... we now use the generic
 * hash table module in Hash.c. We don't need to re-invent the wheel every
 * day -- once in a week is just enough so we don't get rusty.
 * Note: there is only ONE image cache but TWO pixmap caches. This is based
 * on the assumption that there are only few images present that will act
 * as sources for many more pixmaps. So we don't want to waste memory and
 * don't use a backward association cache for images. But as pixmaps are
 * created and destroyed more frequently it makes sense to speed up deletion
 * using a backward association cache. With the help of this cache we can
 * easily lookup a pixmap's description entry having only the pixmap id and
 * the screen id ready at hand.
 */
static LTHashTable ImageCache     = NULL;
static LTHashTable PixmapCache    = NULL;
static LTHashTable RevPixmapCache = NULL;

/*
 * internal functions
 */
static LTImageValue LTGetImageFromCache(char *image_name);
static Boolean LTAddPixmapToCache(char *pixmap_name, Pixmap pixmap, Screen *screen,
				  Pixel foreground, Pixel background,
				  int depth, int width, int height,
				  int hot_x, int hot_y);
static void LTSetupImageCache(void);
static void LTSetupPixmapCache(void);
static void LTCreateSearchPath(void);

/*
 * Various paths used when searching for pixmaps in XmGetPixmap below
 */
static char *_search_path = NULL;

static char *XAPPLRESDIR_set_pattern = "\
%%B:\
%s/%%L/%%T/%%N/%%B:\
%s/%%l/%%T/%%N/%%B:\
%s/%%T/%%N/%%B:\
%s/%%L/%%T/%%B:\
%s/%%l/%%T/%%B:\
%s/%%T/%%B:\
%s/%%T/%%B:\
%s/%%B:\
/usr/lib/X11/%%L/%%T/%%N/%%B:\
/usr/lib/X11/%%l/%%T/%%N/%%B:\
/usr/lib/X11/%%T/%%N/%%B:\
/usr/lib/X11/%%L/%%T/%%B:\
/usr/lib/X11/%%l/%%T/%%B:\
/usr/lib/X11/%%T/%%B:\
/usr/include/X11/%%T/%%B";

static char *nothing_set_pattern = "\
%%B:\
%s/%%L/%%T/%%N/%%B:\
%s/%%l/%%T/%%N/%%B:\
%s/%%T/%%N/%%B:\
%s/%%L/%%T/%%B:\
%s/%%l/%%T/%%B:\
%s/%%T/%%B:\
%s/%%B:\
/usr/lib/X11/%%L/%%T/%%N/%%B:\
/usr/lib/X11/%%l/%%T/%%N/%%B:\
/usr/lib/X11/%%T/%%N/%%B:\
/usr/lib/X11/%%L/%%T/%%B:\
/usr/lib/X11/%%l/%%T/%%B:\
/usr/lib/X11/%%T/%%B:\
/usr/include/X11/%%T/%%B";


/* --------------------------------------------------------------------------
 * Public Image Cache Functions...
 */
/*
 * A pixmap caching function that adds an image to the image cache. This
 * function simply stands on the shoulders of _XmInstallImage() which knows
 * of hot spot coordinates for images.
 */
Boolean
XmInstallImage(XImage *image, char *image_name)
{
    return _XmInstallImage(image, image_name, 0, 0);
} /* XmInstallImage */

/*
 * Almost the same as above, but this sucker can deal with hot spots. This
 * is spooky, isn't it...?!
 */
Boolean
_XmInstallImage(XImage *image, char *image_name, int hot_x, int hot_y)
{
    LTImageValue ImageValue;

    /*
     * First check for bad parameters, then make sure that there's an
     * image cache waiting for our image...
     */
    if ( (image == NULL) || (image_name) == NULL ) {
	return False;
    }
    if ( ImageCache == (LTHashTable) NULL) {
	LTSetupImageCache();
    }
    /*
     * Fill in a description record and let the generic hash table module
     * do the dirty work...
     */
    ImageValue = (LTImageValue) XtMalloc(sizeof(LTImageValueRec));
    ImageValue->image       = image;
    ImageValue->hot_x       = hot_x;
    ImageValue->hot_y       = hot_y;
    ImageValue->undeletable = False;
    if ( !LTHashTableAddItem(ImageCache,
			     (LTHashItemID) image_name,
			     (LTHashItemValue) ImageValue) ) {
        /*
	 * Oops, there's already such an image name used in the cache.
	 * Because the image in the cache hasn't been touched, we now
	 * must free our image value description (the value of an item
	 * within the hash table).
	 */
        XtFree((char *) ImageValue);
        return False;
    }
    return True;
} /* _XmInstallImage */

/*
 * This "foreach" iterator is necessary to find an image by its XImage id.
 * The iterator will free the image value description for the ximage, if
 * it finds one and the image is deletable (i.e. not a pre-defined one).
 */
static LTHashForEachIteratorResult
LTImageIterator(LTHashTable ht,
		LTHashItemID id, LTHashItemValue value, XtPointer image)
{
    if ( ((LTImageValue) value)->image == (XImage *) image ) {
        if ( ((LTImageValue) value)->undeletable ) {
	    return LTHASH_BREAK;
        }
        LTHashTableRemoveItem(ht, id, NULL);
	XtFree((char *) value);
        return LTHASH_COUNTANDBREAK;
    }
    return LTHASH_CONT;
} /* LTImageIterator */

/*
 * Removes an image from the image cache. This implementation is notorously
 * slow if there are *many* images in the cache. Fortunately, the uninstall
 * operation is rarely needed, thus there is (almost) no reason to use a
 * second hash table for image lookup by image pointer. This would only
 * increase memory usage and would have rarely any impact. So we stick with
 * only one hash table and bit the bullet if someone really wants to
 * uninstall an image.
 */
Boolean
XmUninstallImage(XImage *image)
{
    if ( (ImageCache == NULL) || (image == NULL) ) {
        return False;
    }
    return LTHashTableForEachItem(ImageCache, LTImageIterator,
				  (XtPointer) image) != 0 ? True : False;
} /* XmUninstallImage */

/*
 *
 */
Boolean
_XmGetImage(Screen *screen, char *image_name, XImage **image)
{
    return False;
} /* _XmGetImage */

/* --------------------------------------------------------------------------
 * Public Pixmap Cache Functions...
 */
/*
 * A pixmap caching function that generates a pixmap, stores it in a pixmap
 * cache and returns the pixmap.
 */
Pixmap
XmGetPixmap(Screen *screen, char *image_name,
	    Pixel foreground, Pixel background)
{
    Pixmap p;
    p = XmGetPixmapByDepth(screen, image_name, foreground, background,
			   DefaultDepthOfScreen(screen));
    XdbDebug(__FILE__, NULL, "XmGetPixmap(%s, %d, %d) => 0x%x (depth %d)\n",
	     image_name, foreground, background, p,
	     DefaultDepthOfScreen(screen));
    return p;
} /* XmGetPixmap */

/*
 * Almost the same as XmGetPixmap() above, but this time the programmer can
 * even control the depth of the pixmap she/he wants.
 */
Pixmap
XmGetPixmapByDepth(Screen *screen, char *image_name,
		   Pixel foreground, Pixel background, int depth)
{
    LTPixmapDescRec PixmapDesc;
    LTPixmapDesc    PixmapValue;
    LTImageValue    ImageDesc;

    int image_depth;

    char *pathname_to_pixmap;
    unsigned int bitmap_width, bitmap_height;
    int x_hot, y_hot;
    XImage *image;
    Pixmap new_pix, tmp;
    GC gc;
    XGCValues values;

    values.foreground = foreground;
    values.background = background;

    if ( PixmapCache == NULL ) {
        LTSetupPixmapCache();
    }

    if (image_name == NULL)
	return XmUNSPECIFIED_PIXMAP;

    /*
     * First, we check the pixmap cache to see if such a pixmap is already
     * there. Therefore we fill in a pixmap description but only use the
     * first set of fields in the description structure. The second set will
     * contain information about the pixmap, if a pixmap, as described by the
     * first set, is already in the cache.
     */
    PixmapDesc.image_name = image_name;
    PixmapDesc.screen     = screen;
    PixmapDesc.foreground = foreground;
    PixmapDesc.background = background;
    PixmapDesc.depth      = depth;
    if ( LTHashTableLookupItem(PixmapCache, (LTHashItemID) &PixmapDesc,
			       (LTHashItemValue *) &PixmapValue) ) {
        /*
	 * There's a pixmap in the cache so we use it and increment its
	 * reference count.
	 */
        (PixmapValue->ref_count)++;
        return PixmapValue->pixmap;
    }
    /*
     * Okay, we had no luck. Now check the image cache.
     */
    
    ImageDesc = LTGetImageFromCache(image_name);

    if ( ImageDesc != NULL ) {
        /*
	 * A match from the image cache. Now create the pixmap, add it to
	 * the cache, and return it.
	 */
        image = ImageDesc->image;

	if (image->format == XYBitmap)
	    image_depth = 1;
	else
	    image_depth = image->depth;

	/* create the pixmap */
	new_pix = _XmAllocScratchPixmap((XmScreen)XmGetXmScreen(screen),
					depth, image->width, image->height);

	tmp = _XmAllocScratchPixmap((XmScreen)XmGetXmScreen(screen),
				    depth, image->width, image->height);
			   
	gc = XCreateGC(DisplayOfScreen(screen), tmp, 0, NULL);

	/* move the image information into a temporary pixmap */
	XPutImage(DisplayOfScreen(screen),
		  tmp,
		  gc,
		  image,
		  0,0,
		  0,0,
		  image->width, image->height);

	XFreeGC(DisplayOfScreen(screen), gc);

	values.foreground = background;
	values.background = foreground;

	gc = XCreateGC(DisplayOfScreen(screen), new_pix, GCForeground|GCBackground, &values);

	if (image_depth == 1)
	{
	    XCopyPlane(DisplayOfScreen(screen), 
		       tmp, 
		       new_pix,
		       gc, 
		       0,0, 
		       image->width, image->height,
		       0,0, 
		       1);
	}
	else
	{
	    XCopyArea(DisplayOfScreen(screen),
		      tmp,
		      new_pix,
		      gc,
		      0, 0,
		      image->width, image->height,
		      0, 0);
	}

	XFreeGC(DisplayOfScreen(screen), gc);
	
	/* add the pixmap to the cache */
	LTAddPixmapToCache(image_name, new_pix, screen,
			   foreground, background, depth,
			   image->width, image->height,
			   ImageDesc->hot_x, ImageDesc->hot_y);
	return new_pix;
    }

    /* since it wasn't in either cache, we search for the file */
    /* make sure the search path is there */
    if (!_search_path) {
        LTCreateSearchPath();
    }

    if (image_name[0] == '/') /* an absolute pathname */
	pathname_to_pixmap = XtNewString(image_name);
    else
    {
	SubstitutionRec subs[1];

	subs[0].match = 'B';
	subs[0].substitution = XtNewString(image_name);

	pathname_to_pixmap = XtResolvePathname(DisplayOfScreen(screen),
					       "bitmaps",
					       NULL,
					       NULL,
					       _search_path,
					       subs,
					       1,
					       NULL);
        XtFree(subs[0].substitution);
    }					 

    /* this breaks if X isn't where it's supposed to be. Don't dump core,
     * just return */
    if (pathname_to_pixmap == NULL || strlen(pathname_to_pixmap) == 0)
       return XmUNSPECIFIED_PIXMAP;

    XdbDebug(__FILE__, NULL, "pathname found is %s\n", pathname_to_pixmap);
    
    if (XReadBitmapFile(DisplayOfScreen(screen),
			RootWindowOfScreen(screen),
			pathname_to_pixmap,
			&bitmap_width, &bitmap_height,
			&tmp, &x_hot, &y_hot) != BitmapSuccess)
	_XmWarning (NULL, "Couldn't load the pixmap %s.\n", pathname_to_pixmap);

    XtFree(pathname_to_pixmap);

    new_pix = _XmAllocScratchPixmap((XmScreen)XmGetXmScreen(screen),
				    depth, bitmap_width,bitmap_height);

    gc = XCreateGC(DisplayOfScreen(screen), new_pix, GCForeground|GCBackground, &values);

    XCopyPlane(DisplayOfScreen(screen), 
	       tmp, 
	       new_pix,
	       gc, 
	       0,0, 
	       bitmap_width, bitmap_height,
	       0,0, 
	       1);

    XFreeGC(DisplayOfScreen(screen), gc);

    /* add the pixmap to the cache */
    LTAddPixmapToCache(image_name, new_pix, screen,
		       foreground, background, depth,
		       bitmap_width, bitmap_height,
		       0, 0 /* FIXME! hot_x, hot_y! */ );

    return new_pix;
} /* XmGetPixmapByDepth */

/*
 * Removes a pixmap from the pixmap cache.
 */
Boolean
XmDestroyPixmap(Screen *screen, Pixmap pixmap)
{
    LTPixmapDescRec  PixmapDesc;
    LTPixmapDesc     CachedPixmap;
    char            *ImageName;

    if ( PixmapCache == NULL ) {
        LTSetupPixmapCache();
    }

    PixmapDesc.screen = screen;
    PixmapDesc.pixmap = pixmap;

    if ( !LTHashTableLookupItem(RevPixmapCache, (LTHashItemID) &PixmapDesc,
                                (LTHashItemValue *) &CachedPixmap) ) {
        return False;
    }
    if ( --(CachedPixmap->ref_count) > 0 ) {
        return True;
    }

    if ( LTHashTableRemoveItem(RevPixmapCache, (LTHashItemID) &PixmapDesc,
			       (LTHashItemValue *) &CachedPixmap) ) {
        ImageName = CachedPixmap->image_name;
	LTHashTableRemoveItem(PixmapCache, (LTHashItemID) CachedPixmap,
			      NULL);
        XtFree(ImageName);
	return True;
    }
    return False;
} /* XmDestroyPixmap */

/*
 * This one is going to be obsolete in Motif 2.0...
 */
Pixmap
_XmGetPixmap(Screen *screen, char *image_name, int depth,
	     Pixel foreground, Pixel background)
{
    return XmGetPixmapByDepth(screen, image_name,
			      foreground, background, depth);
} /* _XmGetPixmap */

/*
 * Adds a pixmap with the name given to the pixmap cache.
 */
Boolean
_XmInstallPixmap(Pixmap pixmap, Screen *screen, char *image_name,
		 Pixel foreground, Pixel background)
{
    /*
     * Let LTAddPixmapToCache() do the work. Also let it calculate the
     * pixmap's depth and geometry.
     */
    return LTAddPixmapToCache(image_name, pixmap, screen,
			      foreground, background,
			      0, 0, 0, 0, 0);
} /* _XmInstallPixmap */

/*
 * Find pixmap by (screen, pixmap); return True if it's in the cache.
 *
 * Found description of this sucker in McMinds & Whitty, "Writing Your
 * Own OSF/Motif Widgets" HP Professional Books, ISBN 0-13-104191-6,
 * kindly donated to the LessTif project by Linux International.
 */
Boolean
_XmGetPixmapData(Screen *screen, Pixmap pixmap,
		char **image_name,
		int *depth, Pixel *foreground, Pixel *background,
		int *hot_x, int *hot_y,
		unsigned int *width, unsigned int *height)
{
    LTPixmapDescRec PixmapDesc;
    LTPixmapDesc    CachedPixmap;

    if ( PixmapCache == NULL ) {
        LTSetupPixmapCache();
    }

    PixmapDesc.screen = screen;
    PixmapDesc.pixmap = pixmap;
    if ( !LTHashTableLookupItem(RevPixmapCache, (LTHashItemID) &PixmapDesc,
				(LTHashItemValue *) &CachedPixmap) ) {
        return False;
    }
    *image_name = CachedPixmap->image_name;
    *depth      = CachedPixmap->depth;
    *foreground = CachedPixmap->foreground;
    *background = CachedPixmap->background;
    *hot_x      = CachedPixmap->hot_x;
    *hot_y      = CachedPixmap->hot_y;
    *width      = CachedPixmap->width;
    *height     = CachedPixmap->height;
    return True;
} /* _XmGetPixmapData */

/* Motif 2.* version of the above */
Boolean
XmeGetPixmapData(Screen *screen, Pixmap pixmap,
		char **image_name,
		int *depth, Pixel *foreground, Pixel *background,
		int *hot_x, int *hot_y,
		unsigned int *width, unsigned int *height)
{
    return _XmGetPixmapData(screen, pixmap,
			    image_name,
			    depth, foreground, background,
			    hot_x, hot_y, width, height);
} /* XmeGetPixmapData */

/* --------------------------------------------------------------------------
 * --------------------------------------------------------------------------
 * private LessTif functions
 */
/*
 * The pre-defined images live here. They are added to the cache as
 * soon as this sucker gets initialized. Thus there is no need any
 * more to add the default images whenever a XmDisplay gets created.
 */
#define background_width 16
#define background_height 16
static unsigned char background_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

#define f25_width 16
#define f25_height 16
static unsigned char f25_bits[] = {
   0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00,
   0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00,
   0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00};

#define f50_width 16
#define f50_height 16
static unsigned char f50_bits[] = {
   0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa,
   0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa,
   0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa};

#define f75_width 16
#define f75_height 16
static unsigned char f75_bits[] = {
   0xbb, 0xbb, 0xee, 0xee, 0xdd, 0xdd, 0x77, 0x77, 0xbb, 0xbb, 0xee, 0xee,
   0xdd, 0xdd, 0x77, 0x77, 0xbb, 0xbb, 0xee, 0xee, 0xdd, 0xdd, 0x77, 0x77,
   0xbb, 0xbb, 0xee, 0xee, 0xdd, 0xdd, 0x77, 0x77};

#define horizontal_width 16
#define horizontal_height 16
static unsigned char horizontal_bits[] = {
   0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
   0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
   0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00};

#define vertical_width 16
#define vertical_height 16
static unsigned char vertical_bits[] = {
   0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
   0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
   0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};

#define slant_right_width 16
#define slant_right_height 16
static unsigned char slant_right_bits[] = {
   0x11, 0x11, 0x88, 0x88, 0x44, 0x44, 0x22, 0x22, 0x11, 0x11, 0x88, 0x88,
   0x44, 0x44, 0x22, 0x22, 0x11, 0x11, 0x88, 0x88, 0x44, 0x44, 0x22, 0x22,
   0x11, 0x11, 0x88, 0x88, 0x44, 0x44, 0x22, 0x22};

#define slant_left_width 16
#define slant_left_height 16
static unsigned char slant_left_bits[] = {
   0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11,
   0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44,
   0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44};

#define menu_cascade_width 16
#define menu_cascade_height 16
static unsigned char menu_cascade_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f,
   0xff, 0x1f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x1f, 0x00, 0x0f, 0x00, 0x07,
   0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};

#define menu_cascade_rtol_width 16
#define menu_cascade_rtol_height 16
static unsigned char menu_cascade_rtol_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xf0, 0x00,
   0xf8, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xf8, 0xff, 0xf0, 0x00, 0xe0, 0x00,
   0xc0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00};

#define menu_checkmark_width 16
#define menu_checkmark_height 16
static unsigned char menu_checkmark_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x38,
   0x00, 0x1c, 0x00, 0x0e, 0x18, 0x07, 0xb8, 0x03, 0xf8, 0x03, 0xf0, 0x01,
   0xe0, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00};

static struct {
    String         ImageName;
    unsigned char *ImageBits;
} DefaultImages[] = {
    { "background",        background_bits },
    { "25_foreground",     f25_bits },
    { "50_foreground",     f50_bits },
    { "75_foreground",     f75_bits },
    { "horizontal_tile",   horizontal_bits },
    { "vertical_tile",     vertical_bits },
    { "slant_right",       slant_right_bits },
    { "slant_left",        slant_left_bits },
    { "menu_cascade",      menu_cascade_bits },
    { "menu_cascade_rtol", menu_cascade_rtol_bits },
    { "menu_checkmark",    menu_checkmark_bits },
    { NULL,                NULL }
};

/*
 * Initializes the image and pixmap caches. In addition, it puts the default
 * images into the cache.
 */
extern _XInitImageFuncPtrs(XImage *);

static void
LTSetupImageCache(void)
{
    int           i;
    XImage       *Image;
    LTImageValue  ImageValue;

    /*
     * The image cache associates an image name with an XImage pointer and
     * hot spot coordinates. Using the generic hash tables for this makes
     * this really easy.
     */
    ImageCache = LTHashTableCreate(NULL, NULL, LTHASH_ID_STRING);
    /*
     * Now we put the default images into the cache. I'm not using
     * _XmCreateImage() here as this insists on a display pointer. Instead
     * I'm using a self-made XCreateImage() so I don't need a display
     * pointer at all.
     */
    for ( i = 0; DefaultImages[i].ImageName; i++ ) {
	Image = (XImage *) XtCalloc(1, (unsigned) sizeof(XImage));
	Image->width            = 16;
	Image->height           = 16;
	Image->format           = XYBitmap;
	Image->red_mask         = 0;
	Image->green_mask       = 0;
	Image->blue_mask        = 0;
	Image->xoffset          = 0;
	Image->bitmap_pad       = 8;
	Image->depth            = 1;
	Image->data             = (char *) DefaultImages[i].ImageBits;
	Image->bytes_per_line   = (16 + 7) >> 3;
	Image->bits_per_pixel   = 1;

	Image->byte_order       = LSBFirst;
	Image->bitmap_unit      = 8;
	Image->bitmap_bit_order = LSBFirst;
	_XInitImageFuncPtrs(Image);

	ImageValue = (LTImageValue) XtMalloc(sizeof(LTImageValueRec));
	ImageValue->image       = Image;
	ImageValue->hot_x       = 0;
	ImageValue->hot_y       = 0;
	ImageValue->undeletable = True;
        LTHashTableAddItem(ImageCache, 
			   (LTHashItemID) DefaultImages[i].ImageName,
			   (LTHashItemValue) ImageValue);
    }
} /* LTSetupImageCache */


/*
 * The following two functions are needed for the Pixmap cache. They
 * calculate the hash value for an entry and compare two entries. This
 * is done on the tuple (image name, screen, foreground, background, depth).
 */
static unsigned int
LTPixmapGetHash(LTHashItemID id)
{
    LTPixmapDesc  p = (LTPixmapDesc) id;
    unsigned int  hash;
    char         *pStr, ch;

    hash = 0;
    pStr = p->image_name;
    while ( (ch = *pStr++) != 0 ) {
        hash = (hash << 3) + ch;
    }
    hash += (unsigned int) p->screen;
    hash += ((unsigned int) p->foreground) * 71;
    hash += (unsigned int) p->background;
    hash += (unsigned int) p->depth;
    return hash;
} /* LTPixmapGetHash */

static Boolean
LTPixmapCompare(LTHashItemID id1, LTHashItemID id2)
{
    LTPixmapDesc p1 = (LTPixmapDesc) id1;
    LTPixmapDesc p2 = (LTPixmapDesc) id2;

    return (strcmp(p1->image_name, p2->image_name) == 0) &&
           (p1->screen     == p2->screen    ) &&
           (p1->depth      == p2->depth     ) &&
           (p1->foreground == p2->foreground) &&
           (p1->background == p2->background);
} /* LTPixmapCompare */

/*
 * The following two functions are needed for the RevPixmap cache. This
 * is merely a hash table that is used to lookup the information about a
 * particular pixmap using the pixmap and screen ids.
 */
static unsigned int
LTRevPixmapGetHash(LTHashItemID id)
{
    LTPixmapDesc p = (LTPixmapDesc) id;

    return ((unsigned int) p->pixmap) +
           ((unsigned int) p->screen);
} /* LTRevPixmapGetHash */

static Boolean
LTRevPixmapCompare(LTHashItemID id1, LTHashItemID id2)
{
    LTPixmapDesc p1 = (LTPixmapDesc) id1;
    LTPixmapDesc p2 = (LTPixmapDesc) id2;

    return (p1->pixmap == p2->pixmap) && (p1->screen == p2->screen);
} /* LTRevPixmapCompare */

/*
 * Setup the pixmap cache. For reasons of speed and to increase memory
 * usage, we use two hash tables for forward and backward associations.
 * The backward association looks like: (screen, pixmap) ----> (info)
 * We use the same data structure for both parts of the association as
 * they belong together and it makes no sense to allocate them separately.
 * If you look at the code below you should notice that the first hash
 * table takes care of allocating and copying the description data structures
 * into the hash table. So we don't need to take care of allocating and
 * freeing them. For this reason the second (backward) hash table must not
 * do any memory management on the association data (LTHASH_ID_NOCOPY).
 */
static void
LTSetupPixmapCache(void)
{
    PixmapCache = LTHashTableCreate(LTPixmapGetHash, LTPixmapCompare,
				    sizeof(LTPixmapDescRec));
    RevPixmapCache = LTHashTableCreate(LTRevPixmapGetHash, LTRevPixmapCompare,
				       LTHASH_ID_NOCOPY);
} /* LTSetupPixmapCache */

/*
 * Retrieves an image from the cache, given its name. If the image can't be
 * found in the cache, the function simply returns NULL. Otherwise you we'll
 * get back a pointer to the description record of the image within the cache.
 */
static LTImageValue
LTGetImageFromCache(char *image_name)
{
    LTImageValue Image;

    if ( ImageCache == NULL ) {
        LTSetupImageCache();
    }
    if ( LTHashTableLookupItem(ImageCache,
			       image_name, (LTHashItemValue *) &Image) ) {
        return Image;
    }
    return NULL;
} /* LTGetImageFromCache */

/*
 * Adds a named pixmap for particular screen with a bunch of other information
 * to the pixmap cache. There is an important thing to note: this function is
 * ordinarily used to add a pixmap to the cache which is not already in the
 * cache. The reference count for this pixmap then will be "1". If there should
 * be already such a pixmap in the cache, then the old pixmap will be silently
 * lost.
 */
static Boolean
LTAddPixmapToCache(char *pixmap_name, Pixmap pixmap, Screen *screen,
		   Pixel foreground, Pixel background,
		   int depth,
		   int width, int height, int hot_x, int hot_y)
{
    Window       RootWindow;
    int          x, y;
    unsigned int w, h, bw, d;
    LTPixmapDesc PixmapDesc, OldPixmapDesc = NULL;

    if ( pixmap_name == NULL ) {
        return False;
    }
    if ( PixmapCache == NULL ) {
        LTSetupPixmapCache();
    }
    /*
     * For those who always don't now everything, we provide some help...
     */
    if ( (width == 0) || (height == 0) || (depth == 0) ) {
        XGetGeometry(DisplayOfScreen(screen), pixmap,
		     &RootWindow, &x, &y, &w, &h, &bw, &d);
	depth = d; width = w; height = h;
    }

    PixmapDesc = (LTPixmapDesc) XtMalloc(sizeof(LTPixmapDescRec));
    PixmapDesc->image_name = XtNewString(pixmap_name);
    PixmapDesc->screen     = screen;
    PixmapDesc->foreground = foreground;
    PixmapDesc->background = background;
    PixmapDesc->depth      = depth;
    PixmapDesc->pixmap     = pixmap;
    PixmapDesc->width      = width;
    PixmapDesc->height     = height;
    PixmapDesc->hot_x      = hot_x;
    PixmapDesc->hot_y      = hot_y;

    PixmapDesc->ref_count  = 1;

    /*
     * If there's already such an entry in the cache, then we'll silently
     * overwrite it (compatibility with _XmInstallPixmap()). Sigh.
     */
    if ( LTHashTableReplaceItemAndID(PixmapCache, 
				     (LTHashItemID) PixmapDesc,
				     (LTHashItemValue) PixmapDesc,
				     (LTHashItemID *) OldPixmapDesc,
				     NULL) ) {
        XtFree((char *) OldPixmapDesc->image_name);
	XtFree((char *) OldPixmapDesc);
    }
    LTHashTableReplaceItem(RevPixmapCache, (LTHashItemID) PixmapDesc,
			   (LTHashItemValue) PixmapDesc, NULL);

    return True;
} /* LTAddPixmapToCache */


static void
LTCreateSearchPath(void)
{
    char *XBMLANGPATH = XtNewString((String)getenv("XBMLANGPATH"));
    char *XAPPLRESDIR = XtNewString((String)getenv("XAPPLRESDIR"));

    if (XBMLANGPATH) {
	_search_path = XBMLANGPATH;

	if (XAPPLRESDIR) {
	    XtFree(XAPPLRESDIR);
	}
    } else if (XAPPLRESDIR) {
	char *HOME=getenv("HOME");
	int HOME_length = strlen(HOME);
	int XAPPLRESDIR_length = strlen(XAPPLRESDIR);

	_search_path = (char*)XtMalloc(strlen(XAPPLRESDIR_set_pattern)
				       + XAPPLRESDIR_length * 6
				       + HOME_length * 2 + 1);

	sprintf (_search_path, XAPPLRESDIR_set_pattern,
		 XAPPLRESDIR,XAPPLRESDIR,XAPPLRESDIR,
		 XAPPLRESDIR,XAPPLRESDIR,XAPPLRESDIR,
		 HOME,HOME);
		 

    } else { /* neither of them was set... */
	char *HOME=getenv("HOME");
	int HOME_length = strlen(HOME);

	_search_path = (char*)XtMalloc(strlen(nothing_set_pattern)
				       + HOME_length * 7 + 1);

	sprintf (_search_path, nothing_set_pattern,
		 HOME,HOME,HOME,HOME,HOME,HOME,HOME);
    }

    XdbDebug(__FILE__, NULL, "Using %s for search path\n", _search_path);
}

#ifdef NONSTANDARD_EXTENSIONS
/*
 * _XmeGetPixmap is not part of the OSF/Motif API.
 * This routine is used with the string to pixmap converter
 * routine.  It does a lookup into the cache based on the name
 * of the pixmap and also where the foreground, background
 * and depth are equal to zero.
 */
#include<XmI/XmXpm.h>
Pixmap
_XmeGetPixmap(Screen *screen, char *fname)
{
   static Colormap _cmap;
 
   XmXpmAttributes xpm_attrib;
   Pixmap pmap;
   Pixmap mask;

   Display *dpy;
   Window w;
 
   char *pathname_to_pixmap = NULL;

   dpy = DisplayOfScreen(screen);
   w = RootWindowOfScreen(screen);
 
   /* initialize colormap if it hasn't been done */
   if(_cmap == (Colormap) NULL)
   {
      XWindowAttributes w_attrib;
      XGetWindowAttributes(dpy,w,&w_attrib);
      _cmap=w_attrib.colormap;
   }
 
   /*
    * Set the foreground, background and depth all to zero 
    * for now.
    */
   pmap = XmGetPixmapByDepth(screen,fname,0,0,0);
   if (pmap != XmUNSPECIFIED_PIXMAP)
   {
      return pmap;
   }
 
   if(_search_path == NULL)
   {
      LTCreateSearchPath();
   }
 
   /*
    * Attempt to find pixmap in search_path
    * if an absolute path was not given.
    */
   if(fname != NULL && fname[0]=='/')
   {
      pathname_to_pixmap = XtNewString(fname);
   }
   else
   {
      SubstitutionRec sub;
 
      sub.match = 'B';
      sub.substitution = fname;
 
      pathname_to_pixmap = XtResolvePathname(dpy,
					     "bitmaps",
                                             NULL,
                                             NULL,
                                             _search_path,
                                             &sub,
                                             1,
                                             NULL);
   }
 
   if (pathname_to_pixmap == NULL
       || strlen(pathname_to_pixmap) == 0)
   {
      return XmUNSPECIFIED_PIXMAP;
   }
 
   xpm_attrib.colormap=_cmap;
   xpm_attrib.closeness=40000;
   xpm_attrib.valuemask=XmXpmSize | XmXpmReturnPixels
                        | XmXpmColormap | XmXpmCloseness;
 
   if(_XmXpmReadFileToPixmap(dpy,w,pathname_to_pixmap,&pmap,&mask,&xpm_attrib)
      == XmXpmSuccess)
   {
      LTAddPixmapToCache(fname,pmap,screen,0,0,0,0,0,0,0); /* FIXME! */
   }
   else
   {
      /* could not find it so lets return it as unspecified */
      pmap = XmUNSPECIFIED_PIXMAP;
   }
 
   XtFree(pathname_to_pixmap);

   return pmap;
}
#endif
