/* Imf sample list code for the tcl/tk interface to Xconq.
   Copyright (C) 1998, 1999 Stanley T. Shebs.

Xconq 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, or (at your option)
any later version.  See the file COPYING.  */

/* This file implements the "imfsample" widget, which is used for
   displaying a list of given image families in a gridded pattern.
   The case of one image family is useful for displaying images
   embedded in other windows. */

/* Note that this widget depends only on image machinery, and has
   no knowledge of Xconq constructs such as units and sides. */

#include "config.h"
#include "misc.h"
#include "lisp.h"
#include "imf.h"

#include <tcl.h>
#include <tk.h>

#include <math.h>

#include "tkimf.h"

int imfsample_cmd(ClientData cldata, Tcl_Interp *interp,
		  int argc, char *argv[]);

extern Tcl_Interp *interp;

/* MAXROWS, MAXCOLS, MINROWS, and MINCOLS can be changed safely */
#define MAXROWS 50
#define MAXCOLS 16
#define MINROWS 2
#define MINCOLS 8

#define MAXFAMS (MAXROWS*MAXCOLS)

extern int tmp_valid;

extern Display *tmp_display;

static XColor *black_color;
static XColor *white_color;

/* Image family list display widget. */

typedef struct {
    Tk_Window tkwin;		/* Window that embodies the imfsample.  NULL
				 * means window has been deleted but
				 * widget record hasn't been cleaned up yet. */
    Display *display;		/* X's token for the window's display. */
    Tcl_Interp *interp;		/* Interpreter associated with widget. */
    Tcl_Command widgetCmd;	/* Token for imfsample's widget command. */
    int border_width;		/* Width of 3-D border around whole widget. */
    Tk_3DBorder bg_border;	/* Used for drawing background. */
    int relief;			/* Indicates whether window as a whole is
				 * raised, sunken, or flat. */
    GC copygc;			/* Graphics context for copying from
				 * off-screen pixmap onto screen. */
    GC gc;
    int double_buffer;		/* Non-zero means double-buffer redisplay
				 * with pixmap;  zero means draw straight
				 * onto the display. */
    int update_pending;		/* Non-zero means a call to imfsample_display
				 * has already been scheduled. */
 
    int pad;			/* Extra space between images. */
    int iwidth, iheight;
    int show_color;
    int show_names;
    int show_masks;
    int show_bbox;
    int show_solid;

    int width, height;

    XColor *fill_color;

    char *main_imf_name;
    int numimages;
    ImageFamily **imf_list;

    int selected;
    int with_terrain;
    int with_emblem;

    int eltw, elth;
    int rows, cols;

    int numvisrows;
    int firstvisrow;

} Imfsample;

static Tk_ConfigSpec config_specs[] = {
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	"white", Tk_Offset(Imfsample, bg_border), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	"white", Tk_Offset(Imfsample, bg_border), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
	(char *) NULL, 0, 0},
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
	(char *) NULL, 0, 0},
    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
	"0", Tk_Offset(Imfsample, border_width), 0},
    {TK_CONFIG_INT, "-dbl", "doubleBuffer", "DoubleBuffer",
	"1", Tk_Offset(Imfsample, double_buffer), 0},
    {TK_CONFIG_PIXELS, "-height", "height", "Height",
	"32", Tk_Offset(Imfsample, height), 0},
#if 0 /* disabled until we debug */
    {TK_CONFIG_STRING, "-imf", "imf", "Imf",
	"0", Tk_Offset(Imfsample, main_imf_name), 0},
#endif
    {TK_CONFIG_INT, "-iheight", "iheight", "IHeight",
	"0", Tk_Offset(Imfsample, iheight), 0},
    {TK_CONFIG_INT, "-iwidth", "iwidth", "IWidth",
	"0", Tk_Offset(Imfsample, iwidth), 0},
    {TK_CONFIG_INT, "-pad", "pad", "Pad",
	"0", Tk_Offset(Imfsample, pad), 0},
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
	"raised", Tk_Offset(Imfsample, relief), 0},
    {TK_CONFIG_INT, "-showbbox", "showBBox", "ShowBBox",
	"0", Tk_Offset(Imfsample, show_bbox), 0},
    {TK_CONFIG_INT, "-showcolor", "showColor", "ShowColor",
	"1", Tk_Offset(Imfsample, show_color), 0},
    {TK_CONFIG_INT, "-shownames", "showNames", "ShowNames",
	"0", Tk_Offset(Imfsample, show_names), 0},
    {TK_CONFIG_INT, "-showmasks", "showMasks", "ShowMasks",
	"1", Tk_Offset(Imfsample, show_masks), 0},
    {TK_CONFIG_INT, "-showsolid", "showSolid", "ShowSolid",
	"0", Tk_Offset(Imfsample, show_solid), 0},
    {TK_CONFIG_COLOR, "-fillcolor", "fillColor", "FillColor",
	"gray", Tk_Offset(Imfsample, fill_color), 0},
    {TK_CONFIG_PIXELS, "-width", "width", "Width",
	"32", Tk_Offset(Imfsample, width), 0},
    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
	(char *) NULL, 0, 0}
};

static void imfsample_cmd_deleted_proc(ClientData cldata);
static int imfsample_configure(Tcl_Interp *interp, Imfsample *imfsample,
			       int argc, char **argv, int flags);
static void imfsample_destroy(char *memPtr);
static void imfsample_display(ClientData cldata);
static void imfsample_event_proc(ClientData cldata, XEvent *eventPtr);
static int imfsample_widget_cmd(ClientData cldata, Tcl_Interp *interp,
				int argc, char **argv);
static void imfsample_add_imf(Imfsample *imfsample, char *imfname);
static void imfsample_replace_imf(Imfsample *imfsample, char *imfname);
static void imfsample_replace_emblem(Imfsample *imfsample, char *imfname);
static void imfsample_remove_imf(Imfsample *imfsample, char *imfname);
static void draw_one_main_image(Imfsample *imfsample, Drawable d, GC gc,
				ImageFamily *imf,
				int sx, int sy, int sw, int sh);
static void draw_one_image(Imfsample *imfsample, Drawable d, GC gc,
			   Image *img, int sx, int sy, int sw, int sh);

/* The command to create an image sample window. */

int
imfsample_cmd(ClientData cldata, Tcl_Interp *interp, int argc, char **argv)
{
    Tk_Window mainw = (Tk_Window) cldata;
    Imfsample *imfsample;
    Tk_Window tkwin;

    if (argc < 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"",
		argv[0], " pathName ?options?\"", (char *) NULL);
	return TCL_ERROR;
    }

    tkwin = Tk_CreateWindowFromPath(interp, mainw, argv[1], (char *) NULL);
    if (tkwin == NULL)
      return TCL_ERROR;

    Tk_SetClass(tkwin, "Imfsample");

    /* Allocate and initialize the widget record.  */

    imfsample = (Imfsample *) ckalloc(sizeof(Imfsample));
    imfsample->tkwin = tkwin;
    imfsample->display = Tk_Display(tkwin);
    imfsample->interp = interp;
    imfsample->widgetCmd =
      Tcl_CreateCommand(interp,
			Tk_PathName(imfsample->tkwin), imfsample_widget_cmd,
			(ClientData) imfsample, imfsample_cmd_deleted_proc);
    imfsample->border_width = 0;
    imfsample->bg_border = NULL;
    imfsample->relief = TK_RELIEF_FLAT;
    imfsample->copygc = None;
    imfsample->gc = None;
    imfsample->double_buffer = 1;
    imfsample->update_pending = 0;
    imfsample->show_color = 1;
    imfsample->show_names = 0;
    imfsample->show_masks = 0;
    imfsample->fill_color = NULL;
    imfsample->selected = -1;
    imfsample->with_terrain = -1;
    imfsample->with_emblem = -1;

    imfsample->main_imf_name = "";
    imfsample->numimages = 0;
    imfsample->imf_list =
      (ImageFamily **) xmalloc(MAXFAMS * sizeof(ImageFamily *));
    imfsample->numvisrows = 0;
    imfsample->firstvisrow = 0;

    Tk_CreateEventHandler(imfsample->tkwin, ExposureMask|StructureNotifyMask,
			  imfsample_event_proc, (ClientData) imfsample);
    if (imfsample_configure(interp, imfsample, argc-2, argv+2, 0) != TCL_OK) {
	Tk_DestroyWindow(imfsample->tkwin);
	return TCL_ERROR;
    }

    interp->result = Tk_PathName(imfsample->tkwin);
    return TCL_OK;
}

static int
imfsample_widget_cmd(cldata, interp, argc, argv)
ClientData cldata;
Tcl_Interp *interp;
int argc;
char **argv;
{
    Imfsample *imfsample = (Imfsample *) cldata;
    int result = TCL_OK;
    size_t length;
    char c;
    int x, y, col, row, n;

    if (argc < 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"",
		argv[0], " option ?arg arg ...?\"", (char *) NULL);
	return TCL_ERROR;
    }
    Tcl_Preserve((ClientData) imfsample);
    c = argv[1][0];
    length = strlen(argv[1]);
    if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
	&& (length >= 2)) {
	if (argc != 3) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
		    argv[0], " cget option\"",
		    (char *) NULL);
	    goto error;
	}
	result = Tk_ConfigureValue(interp, imfsample->tkwin, config_specs,
		(char *) imfsample, argv[2], 0);
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
	       && (length >= 2)) {
	if (argc == 2) {
	    result = Tk_ConfigureInfo(interp, imfsample->tkwin, config_specs,
		    (char *) imfsample, (char *) NULL, 0);
	} else if (argc == 3) {
	    result = Tk_ConfigureInfo(interp, imfsample->tkwin, config_specs,
		    (char *) imfsample, argv[2], 0);
	} else {
	    result = imfsample_configure(interp, imfsample, argc-2, argv+2,
		    TK_CONFIG_ARGV_ONLY);
	}
    } else if ((c == 'c') && (strncmp(argv[1], "curselection", length) == 0)
	       && (length >= 2)) {
	sprintf(interp->result, "%d", imfsample->selected);
    } else if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)
	       && (length >= 2)) {
	if (strcmp(argv[2], "imf") == 0) {
	    imfsample_add_imf(imfsample, argv[3]);
	} else if (strcmp(argv[2], "all") == 0) {
	    imfsample_add_imf(imfsample, "-all");
	}
    } else if ((c == 'e') && (strncmp(argv[1], "emblem", length) == 0)
	       && (length >= 2)) {
	n = strtol(argv[2], NULL, 10);
	imfsample->with_emblem = n;
    } else if ((c == 'r') && (strncmp(argv[1], "replace", length) == 0)
	       && (length >= 2)) {
	if (strcmp(argv[2], "imf") == 0) {
	    imfsample_replace_imf(imfsample, argv[3]);
	} else if (strcmp(argv[2], "emblem") == 0) {
	    imfsample_replace_emblem(imfsample, argv[3]);
	}
    } else if ((c == 'r') && (strncmp(argv[1], "remove", length) == 0)
	       && (length >= 2)) {
	if (strcmp(argv[2], "imf") == 0) {
	    imfsample_remove_imf(imfsample, argv[3]);
	} else if (strcmp(argv[2], "emblem") == 0) {
	    imfsample->numimages = 1;
	} else if (strcmp(argv[2], "all") == 0) {
	    imfsample->numimages = 0;
	}
    } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
	       && (length >= 2)) {
	x = strtol(argv[2], NULL, 10);
	y = strtol(argv[3], NULL, 10);
	col = x / imfsample->eltw;
	row = y / imfsample->elth + imfsample->firstvisrow;
	n = row * imfsample->cols + col;
	if (n < 0 || n >= imfsample->numimages)
	  n = -1;
	imfsample->selected = n;
    } else if ((c == 't') && (strncmp(argv[1], "terrain", length) == 0)
	       && (length >= 2)) {
	n = strtol(argv[2], NULL, 10);
	imfsample->with_terrain = n;
    } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
    } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
	int count, type, nrow = imfsample->firstvisrow;
	double fraction, fraction2;

	if (argc == 2) {
	    fraction = 0;
	    fraction2 = 1;
	    printf("imfsample yview %g %g\n", fraction, fraction2);
	    sprintf(interp->result, "%g %g", fraction, fraction2);
	} else {
	    type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
	    switch (type) {
	      case TK_SCROLL_ERROR:
		goto error;
	      case TK_SCROLL_MOVETO:
		/*		nsy = ((mapw->vp->symax - mapw->vp->symin) * fraction); */
		break;
	      case TK_SCROLL_PAGES:
		nrow += (count * imfsample->numvisrows * 4) / 5;
		break;
	      case TK_SCROLL_UNITS:
		nrow += count;
		break;
	    }
	    imfsample->firstvisrow = nrow;
	}
    } else {
	Tcl_AppendResult(interp, "bad option \"", argv[1],
		"\": must be cget, configure, position, replace, remove, select",
		(char *) NULL);
	goto error;
    }
    if (!imfsample->update_pending) {
	Tcl_DoWhenIdle(imfsample_display, (ClientData) imfsample);
	imfsample->update_pending = 1;
    }
    Tcl_Release((ClientData) imfsample);
    return result;

error:
    Tcl_Release((ClientData) imfsample);
    return TCL_ERROR;
}

static void
imfsample_add_imf(Imfsample *imfsample, char *imfname)
{
    int i;
    ImageFamily *imf = NULL;

    if (strcmp(imfname, "-all") == 0) {
	imfsample->numimages = 0;
	for (i = 0; i < numimages; ++i) {
	    imfsample->imf_list[imfsample->numimages++] = images[i];
	}
	return;
    }
    imf = tk_find_imf(imfname);
    if (imf == NULL) {
	fprintf(stderr, "Missing imf %s\n", imfname);
	return;
    }
    imfsample->imf_list[imfsample->numimages++] = imf;
}

static void
imfsample_replace_imf(Imfsample *imfsample, char *imfname)
{
    ImageFamily *imf = NULL;

    if (imfsample->numimages == 0) {
	imfsample_add_imf(imfsample, imfname);
	return;
    }
    imf = tk_find_imf(imfname);
    if (imf == NULL) {
	fprintf(stderr, "Missing imf %s\n", imfname);
	return;
    }
    imfsample->imf_list[0] = imf;
}

static void
imfsample_replace_emblem(Imfsample *imfsample, char *imfname)
{
    ImageFamily *imf = NULL;

    if (imfsample->numimages == 1) {
	imfsample_add_imf(imfsample, imfname);
	return;
    }
    imf = tk_find_imf(imfname);
    if (imf == NULL) {
	fprintf(stderr, "Missing imf %s\n", imfname);
	return;
    }
    imfsample->imf_list[1] = imf;
}

static void
imfsample_remove_imf(Imfsample *imfsample, char *imfname)
{
    /* (should look for specific imf) */
    imfsample->numimages = 0;
}

static int
imfsample_configure(Tcl_Interp *interp, Imfsample *imfsample,
		    int argc, char **argv, int flags)
{
    /* Interpret the configuration arguments according to the specs. */
    if (Tk_ConfigureWidget(interp, imfsample->tkwin, config_specs,
			   argc, argv, (char *) imfsample, flags) != TCL_OK)
      return TCL_ERROR;

    /* Set the background for the window and create a graphics context
       for use during redisplay.  */
    Tk_SetWindowBackground(imfsample->tkwin,
			   Tk_3DBorderColor(imfsample->bg_border)->pixel);
    if ((imfsample->copygc == None) && 1/*imfsample->double_buffer*/) {
	XGCValues gcValues;

	gcValues.function = GXcopy;
	gcValues.graphics_exposures = False;
	imfsample->copygc = XCreateGC(imfsample->display,
				      DefaultRootWindow(imfsample->display),
				      GCFunction|GCGraphicsExposures,
				      &gcValues);
    }
    if (imfsample->gc == None) {
	imfsample->gc = XCreateGC(imfsample->display,
				  DefaultRootWindow(imfsample->display),
				  None, NULL);
    }

    /* Register the desired geometry for the window, then arrange for
       the window to be redisplayed.  */
    Tk_GeometryRequest(imfsample->tkwin, imfsample->width, imfsample->height);
    Tk_SetInternalBorder(imfsample->tkwin, imfsample->border_width);
    if (!imfsample->update_pending) {
	Tcl_DoWhenIdle(imfsample_display, (ClientData) imfsample);
	imfsample->update_pending = 1;
    }
    return TCL_OK;
}

static void
imfsample_event_proc(cldata, eventPtr)
ClientData cldata;
XEvent *eventPtr;
{
    Imfsample *imfsample = (Imfsample *) cldata;

    if (eventPtr->type == Expose) {
	if (!imfsample->update_pending) {
	    Tcl_DoWhenIdle(imfsample_display, cldata);
	    imfsample->update_pending = 1;
	}
    } else if (eventPtr->type == ConfigureNotify) {
	if (!imfsample->update_pending) {
	    Tcl_DoWhenIdle(imfsample_display, cldata);
	    imfsample->update_pending = 1;
	}
    } else if (eventPtr->type == DestroyNotify) {
	if (imfsample->tkwin != NULL) {
	    imfsample->tkwin = NULL;
	    Tcl_DeleteCommand(imfsample->interp,
			      Tcl_GetCommandName(imfsample->interp,
						 imfsample->widgetCmd));
	}
	if (imfsample->update_pending) {
	    Tcl_CancelIdleCall(imfsample_display, cldata);
	}
	Tcl_EventuallyFree(cldata, imfsample_destroy);
    }
}

static void
imfsample_cmd_deleted_proc(cldata)
ClientData cldata;
{
    Imfsample *imfsample = (Imfsample *) cldata;
    Tk_Window tkwin = imfsample->tkwin;

    if (tkwin != NULL) {
	imfsample->tkwin = NULL;
	Tk_DestroyWindow(tkwin);
    }
}

static void
imfsample_display(ClientData cldata)
{
    char *str;
    int row, col, namex, namey, n, sx, sy;
    Imfsample *imfsample = (Imfsample *) cldata;
    Display *dpy = imfsample->display;
    Tk_Window tkwin = imfsample->tkwin;
    GC gc;
    Pixmap pm = None;
    Drawable d;
    Tk_Font tkfont;
    int winwidth = Tk_Width(tkwin), winheight = Tk_Height(tkwin);

    imfsample->update_pending = 0;
    if (!Tk_IsMapped(tkwin))
      return;
    /* Create a pixmap for double-buffering if necessary. */
    if (imfsample->double_buffer) {
	pm = Tk_GetPixmap(imfsample->display, Tk_WindowId(tkwin),
			  Tk_Width(tkwin), Tk_Height(tkwin),
			  DefaultDepthOfScreen(Tk_Screen(tkwin)));
	d = pm;
    } else {
	d = Tk_WindowId(tkwin);
    }
    if (black_color == NULL)
      black_color = Tk_GetColor(interp, tkwin, Tk_GetUid("black"));
    if (white_color == NULL)
      white_color = Tk_GetColor(interp, tkwin, Tk_GetUid("white"));
    /* Collect GC and font for subsequent work. */
    gc = imfsample->gc;
    XSetClipMask(dpy, gc, None);
    if (imfsample->show_names) {
      tkfont = Tk_GetFont(interp, tkwin, "-size 10");
      XSetFont(imfsample->display, gc, Tk_FontId(tkfont));
    }


    /* Redraw the widget's background/border. */
    if (imfsample->with_terrain >= 0) {
	ImageFamily *timf = imfsample->imf_list[imfsample->with_terrain];
	Image *timg;
	TkImage *tkimg;

	timg = best_image(timf, 64, 64);
	if (timg != NULL) {
	    tkimg = (TkImage *) timg->hook;
	    if (tkimg != NULL) {
		XSetFillStyle(dpy, gc, FillTiled);
		XSetTile(dpy, gc, tkimg->colr);
		XFillRectangle(dpy, d, gc, 0, 0,
			       Tk_Width(tkwin), Tk_Height(tkwin));
	    }
	}
    } else {
	Tk_Fill3DRectangle(tkwin, d, imfsample->bg_border, 0, 0,
			   Tk_Width(tkwin), Tk_Height(tkwin),
			   imfsample->border_width, imfsample->relief);
    }
#if 0
    for (i = 0; i < imfsample->numimages; i++) {
	if (imf_interp_hook)
	  imfsample->imf_list[i] =
	    (*imf_interp_hook)(imfsample->imf_list[i], NULL, TRUE);
    }
#endif
    /* Tweak the default item width/height to something better. */
    if (imfsample->iheight == 0) {
	imfsample->iheight = 32;
	if (imfsample->numimages == 1)
	  imfsample->iheight = imfsample->height;
    }
    if (imfsample->iwidth == 0) {
	imfsample->iwidth = 32;
	if (imfsample->numimages == 1)
	  imfsample->iwidth = imfsample->width;
    }
    imfsample->eltw = imfsample->iwidth + 2 * imfsample->pad;
    if (imfsample->show_names)
      imfsample->eltw += 80;
    imfsample->elth = imfsample->iheight + 2 * imfsample->pad;
    /* Fix a lower bound on the vertical spacing. */
    /* (should be determined by choice of app font) */
    if (imfsample->elth < 10)
      imfsample->elth = 10;
    /* Compute and save the number of columns to use. */
    imfsample->cols = winwidth / imfsample->eltw;
    if (imfsample->cols <= 0)
      imfsample->cols = 1;
    /* We can get a little wider spacing by recalculating the element
       width. */
    if (imfsample->show_names)
      imfsample->eltw = (winwidth - 10) / imfsample->cols;
    imfsample->rows = imfsample->numimages / imfsample->cols;
    /* Account for a last partial row. */
    if (imfsample->rows * imfsample->cols < imfsample->numimages)
      ++(imfsample->rows);
    /* Compute the number of visible rows.  It would be time-consuming
       to render all the images, so try to do only the visible ones. */
    imfsample->numvisrows = winheight / imfsample->elth;
    if (imfsample->numvisrows > imfsample->rows)
      imfsample->numvisrows = imfsample->rows;
    if (imfsample->numvisrows < 1)
      imfsample->numvisrows = min(1, imfsample->rows);
    if (imfsample->firstvisrow + imfsample->numvisrows > imfsample->rows)
      imfsample->firstvisrow = imfsample->rows - imfsample->numvisrows;
    /* Now iterate through all the images we want to draw. */
    for (row = imfsample->firstvisrow; row < (imfsample->firstvisrow + imfsample->numvisrows); ++row) {
	for (col = 0; col < imfsample->cols; ++col) {
	    n = row * imfsample->cols + col;
	    if (n >= imfsample->numimages)
	      break;
	    sx = col * imfsample->eltw;
	    sy = (row - imfsample->firstvisrow) * imfsample->elth;
	    draw_one_main_image(imfsample, d, gc, imfsample->imf_list[n],
			   sx + imfsample->pad, sy + imfsample->pad,
			   imfsample->iwidth, imfsample->iheight);
	    if (imfsample->show_names) {
		namex = sx + imfsample->pad + imfsample->iwidth + 2;
		namey = sy + (imfsample->elth / 2) + 5;
		XSetClipMask(dpy, gc, None);
		XSetFillStyle(dpy, gc, FillSolid);
		/* Draw the text in black only. */
		/* (should invent a way to make show up when black bg; an
		   offset version in white is semi-OK, but hard on eyes) */
		XSetForeground(dpy, gc, black_color->pixel);
		str = imfsample->imf_list[n]->name;
		Tk_DrawChars(dpy, d, gc, tkfont, str, strlen(str),
			     namex, namey);
	    }
	    /* Indicate the selected imf. */
	    if (n == imfsample->selected) {
		XSetClipMask(dpy, gc, None);
		XSetFillStyle(dpy, gc, FillSolid);
		XSetForeground(dpy, gc, black_color->pixel);
		XDrawRectangle(dpy, d, gc, sx, sy,
			       imfsample->eltw, imfsample->elth);
	    }
	}
    }
    /* If double-buffered, copy to the screen and release the pixmap.  */
    if (imfsample->double_buffer) {
	XCopyArea(imfsample->display, pm, Tk_WindowId(tkwin),
		  imfsample->copygc,
		  0, 0, (unsigned) winwidth, (unsigned) winheight, 0, 0);
	Tk_FreePixmap(imfsample->display, pm);
    }
    if (imfsample->show_names)
      Tk_FreeFont(tkfont);
}

static void
imfsample_destroy(char *ptr)
{
    Imfsample *imfsample = (Imfsample *) ptr;

    Tk_FreeOptions(config_specs, (char *) imfsample, imfsample->display, 0);
    if (imfsample->copygc != None)
      XFreeGC(imfsample->display, imfsample->copygc);
    if (imfsample->gc != None)
      XFreeGC(imfsample->display, imfsample->gc);
    ckfree((char *) imfsample);
}

/* Draw a single image plus optional emblem at a given location. */

static void
draw_one_main_image(Imfsample *imfsample, Drawable d, GC gc, ImageFamily *imf,
		    int sx, int sy, int sw, int sh)
{
    ImageFamily *eimf;
    Image *img, *eimg;
    int ex, ey, ew, eh;

    img = best_image(imf, sw, sh);
    if (img == NULL)
      return;
    draw_one_image(imfsample, d, gc, img, sx, sy, sw, sh);
    if (imfsample->with_emblem >= 0) {
	eimf = imfsample->imf_list[imfsample->with_emblem];
	if (eimf != NULL
	    && emblem_position(img, eimf->name, eimf, sw, sh,
			       &ex, &ey, &ew, &eh)) {
	  eimg = best_image(eimf, ew, eh);
	  if (eimg == NULL)
	    return;
	  draw_one_image(imfsample, d, gc, eimg, sx + ex, sy + ey, ew, eh);
	}
    }
}

/* Draw a single image at a given location. */

static void
draw_one_image(Imfsample *imfsample, Drawable d, GC gc, Image *img,
	       int sx, int sy, int sw, int sh)
{
    int sx2, sy2;
    TkImage *tkimg;
    Tk_Window tkwin = imfsample->tkwin;
    Display *dpy = Tk_Display(tkwin);
    XColor *imagecolor = black_color;
    XColor *maskcolor = white_color;

    if (!img->istile) {
	/* Offset the image to draw in the middle of its area,
	   whether larger or smaller than the given area. */
	sx2 = sx + (sw - img->w) / 2;  sy2 = sy + (sh - img->h) / 2;
	/* Only change the size of the rectangle being drawn if it's
	   smaller than what was passed in. */
	if (img->w < sw) {
	    sx = sx2;
	    sw = img->w;
	}
	if (img->h < sh) {
	    sy = sy2;
	    sh = img->h;
	}
    }
    tkimg = (TkImage *) img->hook;
    if (tkimg == NULL)
      return;
    XSetClipMask(dpy, gc, None);
    if (tkimg->colr != None && imfsample->show_color) {
	if (img->istile) {
	    XSetFillStyle(dpy, gc, FillTiled);
	    XSetTile(dpy, gc, tkimg->colr);
	    XFillRectangle(dpy, d, gc, sx, sy, sw, sh);
	} else {
	  if (use_clip_mask) {
	    if (tkimg->mask != None && imfsample->show_masks) {
	        XSetClipOrigin(dpy, gc, sx2, sy2);
		/* Use the image's mask to do clipped drawing. */
		XSetClipMask(dpy, gc, tkimg->mask);
	    }
	  } else {
	    if (tkimg->mask != None && imfsample->show_masks) {
	      XSetFunction(dpy, gc, GXand);
	      XCopyArea(dpy, tkimg->mask, d, gc, 0, 0, sw, sh, sx, sy);
	    }
	    XSetFunction(dpy, gc, GXor);
	  }
	  /* Draw the color image. */
	  XCopyArea(dpy, tkimg->colr, d, gc, 0, 0, sw, sh, sx, sy);
	  if (!use_clip_mask)
	    XSetFunction(dpy, gc, GXcopy);
	}
    } else if (tkimg->solid != NULL && imfsample->show_color) {
	XSetFillStyle(dpy, gc, FillSolid);
	XSetForeground(dpy, gc, tkimg->solid->pixel);
	XFillRectangle(dpy, d, gc, sx, sy, sw, sh);
    } else if (tkimg->mono != None || tkimg->mask != None) {
	if (use_clip_mask) {
	  XSetFillStyle(dpy, gc, FillSolid);
	  /* Set the color we're going to use for the mask; use the
	     imagecolor if we'll be using the mask as the only image. */
	  XSetForeground(dpy, gc,
			 (tkimg->mono == None ? imagecolor : maskcolor)->pixel);
	  XSetClipOrigin(dpy, gc, sx2, sy2);
	  /* Set the clip mask to be explicit mask or unit's image. */
	  if (tkimg->mask && imfsample->show_masks)
	    XSetClipMask(dpy, gc, tkimg->mask);
	  else
	    XSetClipMask(dpy, gc, tkimg->mono);
	  /* Draw the mask. */
	  XFillRectangle(dpy, d, gc, sx, sy, sw, sh);
	  /* Draw the image proper. */
	  if (tkimg->mono != None) {
	    XSetForeground(dpy, gc, imagecolor->pixel);
	    XSetClipMask(dpy, gc, tkimg->mono);
	    XFillRectangle(dpy, d, gc, sx, sy, sw, sh);
	  }
	} else {
	  if (tkimg->mask != None && imfsample->show_masks) {
	    XSetFunction(dpy, gc, GXand);
	    XCopyArea(dpy, tkimg->mask, d, gc, 0, 0, sw, sh, sx, sy);
	  }
	  if (tkimg->mono != None) {
	    XSetFunction(dpy, gc, GXor);
	    XCopyArea(dpy, tkimg->mono, d, gc, 0, 0, sw, sh, sx, sy);
	  }
	  XSetFunction(dpy, gc, GXcopy);
	}
    }
}
