#include <gtk/gtk.h>
#include <gdk/gdkrgb.h>
#include <gdk/gdkkeysyms.h>

#include "guiutils.h"
#include "guirgbimg.h"
#include "statictip.h"

#include "tlist.h"


/* Callbacks */
static gint TListConfigureEventCB(
	GtkWidget *widget, GdkEventConfigure *configure, gpointer data
);
static gint TListExposeEventCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
);
static gint TListKeyEventCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
);
static gint TListButtonEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);
static gint TListMotionEventCB(
	GtkWidget *widget, GdkEventMotion *motion, gpointer data
);
static gint TListCrossingEventCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);
static gint TListFocusEventCB(
	GtkWidget *widget, GdkEventFocus *focus, gpointer data
);

static void TListAdjustmentValueChangedCB(
	GtkAdjustment *adjustment, gpointer data
);


/* Thumb Utilities */
static tlist_thumb_struct *TListGetThumbPtr(
	tlist_struct *tlist, gint thumb_num
);
static gboolean TListIsThumbSelected(
	tlist_struct *tlist, gint thumb_num
);


/* Thumbs */
static tlist_thumb_struct *TListThumbNew(void);
static void TListThumbDelete(tlist_thumb_struct *thumb);


/* Selecting */
static void TListDoSelectThumb(
	tlist_struct *tlist, gint thumb_num, GdkEventButton *button,
	gboolean append
);
static void TListDoSelectThumbRange(
	tlist_struct *tlist, gint thumb_num, GdkEventButton *button
);
static void TListDoUnselectThumb(
	tlist_struct *tlist, gint thumb_num, GdkEventButton *button
);
static void TListDoSelectAllThumbs(tlist_struct *tlist);
static void TListDoUnselectAllThumbs(tlist_struct *tlist);


/* Misc */
void TListQueryThumbPixmapSize(
	tlist_struct *tlist,
	gint img_width, gint img_height,
	gint *width, gint *height
);


/* Drawing */
void TListDraw(tlist_struct *tlist);
void TListQueueDraw(tlist_struct *tlist);


/* Resize */
void TListResize(tlist_struct *tlist, gint width, gint height);


/* Freeze/Thaw */
void TListFreeze(tlist_struct *tlist);
void TListThaw(tlist_struct *tlist);


/* Thumbs Add, Set, and Delete */
static GtkVisibility TListThumbTextVisiblity(
	tlist_struct *tlist, const gchar *text
);
gint TListInsert(tlist_struct *tlist, gint thumb_num, const gchar *text);
gint TListAppend(tlist_struct *tlist, const gchar *text);
void TListSetLoadState(
	tlist_struct *tlist, gint thumb_num,
	tlist_load_state load_state
);
void TListSetText(
	tlist_struct *tlist, gint thumb_num,
	const gchar *text
);
void TListSetPixmap(
	tlist_struct *tlist, gint thumb_num,
	GdkPixmap *pixmap, GdkBitmap *mask
);
void TListSetRGBA(
	tlist_struct *tlist, gint thumb_num,
	gint width, gint height,	/* Of image data */
	gint bpl,                       /* Bytes per line, can be -1 */
	GdkRgbDither dith,
	const guint8 *rgba,
	gboolean no_enlarge		/* Do not upscale if image smaller
					 * than thumb */
);
void TListSetThumbData(
	tlist_struct *tlist, gint thumb_num,
	gpointer data   
);
void TListSetThumbDataFull(
	tlist_struct *tlist, gint thumb_num,
	gpointer data,
	void (*destroy_cb)(gpointer) 
);
void TListRemove(tlist_struct *tlist, gint thumb_num);
void TListClear(tlist_struct *tlist);

/* Thumbs Get */
tlist_load_state TListGetLoadState(tlist_struct *tlist, gint thumb_num);
gboolean TListGetText(
	tlist_struct *tlist, gint thumb_num, gchar **text
);
gboolean TListGetPixmap(
	tlist_struct *tlist, gint thumb_num,
	GdkPixmap **pixmap, GdkBitmap **mask
);
gpointer TListGetThumbData(tlist_struct *tlist, gint thumb_num);

/* Thumbs Find */
gint TListFindThumbFromData(tlist_struct *tlist, gpointer data);

/* Selecting */
void TListSelectThumb(tlist_struct *tlist, gint thumb_num);
void TListUnselectThumb(tlist_struct *tlist, gint thumb_num);
void TListSelectAll(tlist_struct *tlist);
void TListUnselectAll(tlist_struct *tlist);

gboolean TListGetSelection(
	tlist_struct *tlist, gint x, gint y,
	gint *thumb_num, gint *thumb_ix, gint *thumb_iy
);
gboolean TListGetPosition(
	tlist_struct *tlist, gint thumb_num,
	gint *x, gint *y
);


/* Visibility */
GtkVisibility TListIsThumbVisible(tlist_struct *tlist, gint thumb_num);


/* Scrolling */
void TListMoveTo(
	tlist_struct *tlist, gint thumb_num, gfloat coeff
);


/* Thumbs List */
tlist_struct *TListNew(
	gboolean horizontal,
	gint thumb_width, gint thumb_height, gint thumb_border,
	gpointer client_data,
	void (*select_cb)(gpointer, GdkEventButton *, gint, gpointer),
	void (*unselect_cb)(gpointer, GdkEventButton *, gint, gpointer)
);
void TListThumbGeometry(
	tlist_struct *tlist, 
	gint thumb_width, gint thumb_height, gint thumb_border
);
void TListSelectionMode(
	tlist_struct *tlist, GtkSelectionMode selection_mode
);
void TListDoubleBuffer(tlist_struct *tlist, gboolean double_buffer);
void TListOrientation(tlist_struct *tlist, gboolean horizontal); 
void TListShowThumbFrames(tlist_struct *tlist, gboolean show);
void TListShowThumbLabels(tlist_struct *tlist, gboolean show);
void TListShowTextTips(tlist_struct *tlist, gboolean show);
void TListMap(tlist_struct *tlist);
void TListUnmap(tlist_struct *tlist);
void TListDelete(tlist_struct *tlist);


/* Size of GtkFrame borders */
#define DEF_FRAME_WIDTH		2
#define DEF_FRAME_HEIGHT	2


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	Thumbs List GtkDrawingArea "configure_event" signal callback.
 */
static gint TListConfigureEventCB(
	GtkWidget *widget, GdkEventConfigure *configure, gpointer data
)
{
	tlist_struct *tlist = TLIST(data);
	if((widget == NULL) || (configure == NULL) || (tlist == NULL))
	    return(FALSE);

	TListResize(tlist, configure->width, configure->height);

	return(TRUE);
}

/*
 *	Thumbs List GtkDrawingArea "expose_event" signal callback.
 */
static gint TListExposeEventCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
	tlist_struct *tlist = TLIST(data);
	if((widget == NULL) || (expose == NULL) || (tlist == NULL))
	    return(FALSE);

	TListDraw(tlist);

	return(TRUE);
}


/*
 *	Thumbs List GtkDrawingArea "button_press_event" or
 *	"button_release_event" signal callback.
 */
static gint TListButtonEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	gint thumb_num;
	tlist_struct *tlist = TLIST(data);
	if((widget == NULL) || (button == NULL) || (tlist == NULL))
	    return(status);

	switch((gint)button->type)
	{
	  case GDK_BUTTON_PRESS:
	    /* Grab focus as needed */
	    if(!GTK_WIDGET_HAS_FOCUS(widget))
		gtk_widget_grab_focus(widget);

	    /* Handle by button number */
	    switch(button->button)
	    {
	      case 1:		/* Select/unselect thumb */
		/* Match the thumb the button was pressed over */
		if(TListGetSelection(
		    tlist, button->x, button->y,
		    &thumb_num, NULL, NULL
		))
		{
		    TListFreeze(tlist);

		    /* Set focus thumb */
		    tlist->focus_thumb = thumb_num;

		    /* Select/unselect thumb(s) by key modifier */
		    /* Control Key (Invert Individual Selection) */
		    if(button->state & GDK_CONTROL_MASK)
		    {
			switch(tlist->selection_mode)
			{
			  case GTK_SELECTION_EXTENDED:
			  case GTK_SELECTION_MULTIPLE:
			    if(TListIsThumbSelected(tlist, thumb_num))
				TListDoUnselectThumb(
				    tlist, thumb_num, button
				);
			    else
				TListDoSelectThumb(
				    tlist, thumb_num, button, TRUE
				);
			    break;
			  case GTK_SELECTION_BROWSE:
			    TListDoSelectThumb(
				tlist, thumb_num, button, FALSE
			    );
			    break;
			  case GTK_SELECTION_SINGLE:
			    if(TListIsThumbSelected(tlist, thumb_num))
			    {
				TListDoUnselectAllThumbs(tlist);
			    }
			    else
			    {
				TListDoUnselectAllThumbs(tlist);
				TListDoSelectThumb(
				    tlist, thumb_num, button, TRUE
				);
			    }
			    break;
			}
		    }
		    /* Shift Key (Group Selection) */
		    else if(button->state & GDK_SHIFT_MASK)
		    {
			switch(tlist->selection_mode)
			{
			  case GTK_SELECTION_EXTENDED:
			  case GTK_SELECTION_MULTIPLE:
			    TListDoSelectThumbRange(
				tlist, thumb_num, button
			    );
			    break;
			  case GTK_SELECTION_BROWSE:
			  case GTK_SELECTION_SINGLE:
			    if(TListIsThumbSelected(tlist, thumb_num))
			    {
				TListDoUnselectAllThumbs(tlist);
			    }
			    else
			    {
				TListDoUnselectAllThumbs(tlist);
				TListDoSelectThumb(
				    tlist, thumb_num, button, TRUE
				);
			    }
			    break;
			}
		    }
		    /* No important modifier keys */
		    else
		    {
			/* If there was exactly one thumb selected and
			 * it matches thumb_num then do nothing
			 */
			GList	*glist,
				*selection = g_list_copy(tlist->selection);

			/* Unselect all thumbs except for the thumb
			 * indicated by thumb_num
			 */
			for(glist = selection;
			    glist != NULL;
			    glist = g_list_next(glist)
			)
			{
			    if((gint)glist->data != thumb_num)
				TListDoUnselectThumb(
				    tlist, (gint)glist->data, NULL
				    );
			}
			TListDoSelectThumb(
			    tlist, thumb_num, button, TRUE
			);

			g_list_free(selection);
		    }

		    TListThaw(tlist);
		}
		break;
	    }
	    status = TRUE;
	    break;
	}

	return(status);
}

/*
 *	Thumbs List GtkDrawingArea "motion_notify_event" signal
 *	callback.
 */
static gint TListMotionEventCB(
	GtkWidget *widget, GdkEventMotion *motion, gpointer data
)
{
	gint status = FALSE;
	gint x, y, thumb_num;
	tlist_thumb_struct *thumb;
	tlist_struct *tlist = TLIST(data);
	if((widget == NULL) || (motion == NULL) || (tlist == NULL))
	    return(status);

	if(motion->is_hint)
	{
	    GdkModifierType mask;
	    gdk_window_get_pointer(widget->window, &x, &y, &mask);
	}
	else
	{
	    x = (gint)motion->x;
	    y = (gint)motion->y;
	}

	/* Match the thumb the motion was pressed over */
	if(!TListGetSelection(tlist, x, y, &thumb_num, NULL, NULL))
	    thumb_num = -1;

	thumb = ((thumb_num >= 0) && (thumb_num < tlist->total_thumbs)) ?
	    tlist->thumb[thumb_num] : NULL;

	if(thumb != NULL)
	{
	    /* Check if the thumb that the motion was over is different
	     * from the thumb that the pointed was over
	     */
	    if(tlist->pointer_over_thumb != thumb_num)
	    {
		/* Update the thumb that the pointer is currently
		 * over
		 */
		tlist->pointer_over_thumb = thumb_num;

		/* Thumb text not fully visible? */
		if((tlist->flags & TLIST_SHOW_TEXTTIPS) &&
		   (thumb->text_visibility != GTK_VISIBILITY_FULL) &&
		   (thumb->text != NULL)
		)
		{
		    gint x, y;
		    const gchar *text = thumb->text;
		    GtkStyle *style = gtk_widget_get_style(widget);
		    GdkFont *font = (style != NULL) ? style->font : NULL;
		    GdkTextBounds b;

		    gdk_string_bounds(font, text, &b);

		    if(TListGetPosition(tlist, thumb_num, &x, &y))
		    {
			x -= ((b.width + (2 * 4)) - tlist->thumb_width) / 2;
			y += tlist->thumb_height;

			/* Set and display thumb tip */
			StaticTipSet(
			    widget, text,
			    STATIC_TIP_ALIGN_WIDGET_VALUE,
			    STATIC_TIP_ALIGN_WIDGET_VALUE,
			    x, y
			);
		    }
		}
		else
		{
		    /* Hide thumb tip */
		    StaticTipSet(widget, NULL, 0, 0, 0, 0);
		}

		/* Queue redraw */
		TListQueueDraw(tlist);
	    }
	}
	else
	{
	    /* Motion did not occure over a thumb */
	    if(tlist->pointer_over_thumb > -1)
	    {
		tlist->pointer_over_thumb = -1;

		/* Hide thumb tip */
		StaticTipSet(widget, NULL, 0, 0, 0, 0);

		/* Queue redraw */
		TListQueueDraw(tlist);
	    }
	}
	status = TRUE;

	return(status);
}

/*
 *	Thumbs List GtkDrawingArea "key_press_event" or
 *	"key_release_event" signal callback.
 */
static gint TListKeyEventCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	gboolean press;
	guint keyval, state;
	tlist_struct *tlist = TLIST(data);
	if((key == NULL) || (tlist == NULL))
	    return(status);

	etype = key->type;
	press = (etype == GDK_KEY_PRESS) ? TRUE : FALSE;
	keyval = key->keyval;
	state = key->state;

#define DO_STOP_KEY_SIGNAL_EMIT	{		\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget),				\
  press ?					\
   "key_press_event" : "key_release_event"	\
 );						\
}
#define DO_ADJ_CLAMP_EMIT	{		\
 if(adj->value > (adj->upper - adj->page_size))	\
  adj->value = adj->upper - adj->page_size;	\
						\
 if(adj->value < adj->lower)			\
  adj->value = adj->lower;			\
						\
 gtk_signal_emit_by_name(			\
  GTK_OBJECT(adj), "value_changed"		\
 );						\
}
/* Clamp the focus thumb and queue redraw */
#define DO_TLIST_FOCUS_THUMB_CLAMP_DRAW	{	\
 if(tlist->focus_thumb >= tlist->total_thumbs)	\
  tlist->focus_thumb = tlist->total_thumbs - 1;	\
 if(tlist->focus_thumb < 0)			\
  tlist->focus_thumb = 0;			\
 TListQueueDraw(tlist);				\
}

	/* Check which widget this event is for */

	/* Thumbs List GtkDrawingArea */
	if(widget == tlist->list_da)
	{
	    /* Handle by key value */
	    switch(keyval)
	    {
	      case GDK_space:
		if(press)
		{
		    gint thumb_num = tlist->focus_thumb;

		    TListFreeze(tlist);

		    switch(tlist->selection_mode)
		    {
		      case GTK_SELECTION_EXTENDED:
		      case GTK_SELECTION_MULTIPLE:
			if(TListIsThumbSelected(tlist, thumb_num))
			    TListDoUnselectThumb(
				tlist, thumb_num, NULL
			    );
			else
			    TListDoSelectThumb(
				tlist, thumb_num, NULL, TRUE
			    );
			break;

		      case GTK_SELECTION_BROWSE:
			TListDoSelectThumb(
			    tlist, thumb_num, NULL, FALSE
			);
			break;

		      case GTK_SELECTION_SINGLE:
			if(TListIsThumbSelected(tlist, thumb_num))
			{
			    TListDoUnselectAllThumbs(tlist);
			}
			else
			{
			    TListDoUnselectAllThumbs(tlist);
			    TListDoSelectThumb(
				tlist, thumb_num, NULL, TRUE
			    );
			}
			break;
		    }

		    TListThaw(tlist);
		}
		break;


	      case GDK_Up:
	      case GDK_KP_Up:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    /* Get adjustment and scroll up */
		    GtkAdjustment *adj = tlist->vadj;
		    if(adj != NULL)
		    {
			adj->value -= adj->step_increment;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if((state & GDK_SHIFT_MASK) && press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb up */
		    tlist->focus_thumb -= (tlist->flags & TLIST_HORIZONTAL) ?
			1 : tlist->thumbs_per_line;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    switch(tlist->selection_mode)
		    {
		      case GTK_SELECTION_EXTENDED:
		      case GTK_SELECTION_MULTIPLE:
			TListDoSelectThumbRange(
			    tlist, tlist->focus_thumb, NULL
			);
			break;

		      case GTK_SELECTION_BROWSE:
		      case GTK_SELECTION_SINGLE:
			TListDoUnselectAllThumbs(tlist);
			TListDoSelectThumb(
			    tlist, tlist->focus_thumb, NULL, TRUE
			);
			break;
		    }

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 0.0);

		    TListThaw(tlist);
		}
		else if(press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb up */
		    tlist->focus_thumb -= (tlist->flags & TLIST_HORIZONTAL) ?
			1 : tlist->thumbs_per_line;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 0.0);

		    TListThaw(tlist);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Down:
	      case GDK_KP_Down:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    /* Get adjustment and scroll down */
		    GtkAdjustment *adj = tlist->vadj;
		    if(adj != NULL)
		    {
			adj->value += adj->step_increment;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if((state & GDK_SHIFT_MASK) && press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb down */
		    tlist->focus_thumb += (tlist->flags & TLIST_HORIZONTAL) ?
			1 : tlist->thumbs_per_line;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    switch(tlist->selection_mode)
		    {
		      case GTK_SELECTION_EXTENDED:
		      case GTK_SELECTION_MULTIPLE:
			TListDoSelectThumbRange(
			    tlist, tlist->focus_thumb, NULL
			);
			break;

		      case GTK_SELECTION_BROWSE:
		      case GTK_SELECTION_SINGLE:
			TListDoUnselectAllThumbs(tlist);
			TListDoSelectThumb(
			    tlist, tlist->focus_thumb, NULL, TRUE
			);
			break;
		    }

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 1.0);

		    TListThaw(tlist);
		}
		else if(press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb down */
		    tlist->focus_thumb += (tlist->flags & TLIST_HORIZONTAL) ?
			1 : tlist->thumbs_per_line;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 1.0);

		    TListThaw(tlist);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Left:
	      case GDK_KP_Left:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    /* Get adjustment and scroll left */
		    GtkAdjustment *adj = tlist->hadj;
		    if(adj != NULL)
		    {
			adj->value -= adj->step_increment;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if((state & GDK_SHIFT_MASK) && press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb left */
		    tlist->focus_thumb -= (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->thumbs_per_line : 1;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    switch(tlist->selection_mode)
		    {
		      case GTK_SELECTION_EXTENDED:
		      case GTK_SELECTION_MULTIPLE:
			TListDoSelectThumbRange(
			    tlist, tlist->focus_thumb, NULL
			);
			break;

		      case GTK_SELECTION_BROWSE:
		      case GTK_SELECTION_SINGLE:
			TListDoUnselectAllThumbs(tlist);
			TListDoSelectThumb(
			    tlist, tlist->focus_thumb, NULL, TRUE
			);
			break;
		    }

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 0.0);

		    TListThaw(tlist);
		}
		else if(press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb left */
		    tlist->focus_thumb -= (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->thumbs_per_line : 1;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 0.0);

		    TListThaw(tlist);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Right:
	      case GDK_KP_Right:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    /* Get adjustment and scroll right */
		    GtkAdjustment *adj = tlist->hadj;
		    if(adj != NULL)
		    {
			adj->value += adj->step_increment;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if((state & GDK_SHIFT_MASK) && press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb right */
		    tlist->focus_thumb += (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->thumbs_per_line : 1;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    switch(tlist->selection_mode)
		    {
		      case GTK_SELECTION_EXTENDED:
		      case GTK_SELECTION_MULTIPLE:
			TListDoSelectThumbRange(
			    tlist, tlist->focus_thumb, NULL
			);
			break;

		      case GTK_SELECTION_BROWSE:
		      case GTK_SELECTION_SINGLE:
			TListDoUnselectAllThumbs(tlist);
			TListDoSelectThumb(
			    tlist, tlist->focus_thumb, NULL, TRUE
			);
			break;
		    }

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 1.0);

		    TListThaw(tlist);
		}
		else if(press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb right */
		    tlist->focus_thumb += (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->thumbs_per_line : 1;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 1.0f);

		    TListThaw(tlist);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Page_Up:
	      case GDK_KP_Page_Up:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    GtkAdjustment *adj = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->hadj : tlist->vadj;
		    if(adj != NULL)
		    {
			adj->value -= adj->page_increment;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if(press)
		{
		    gint thumb_len = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->thumb_width : tlist->thumb_height;
		    GtkAdjustment *adj = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->hadj : tlist->vadj;
		    if((adj != NULL) && (thumb_len > 0))
		    {
			gint n = (gint)(adj->page_increment / thumb_len) *
			    tlist->thumbs_per_line;

			TListFreeze(tlist);

			/* Move focus thumb */
			tlist->focus_thumb -= n;
			DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

			/* Select thumbs in between */
			if(state & GDK_SHIFT_MASK)
			{
			    switch(tlist->selection_mode)
			    {
			      case GTK_SELECTION_EXTENDED:
			      case GTK_SELECTION_MULTIPLE:
				TListDoSelectThumbRange(
				    tlist, tlist->focus_thumb, NULL 
				);
				break;

			      case GTK_SELECTION_BROWSE:
			      case GTK_SELECTION_SINGLE:
				TListDoUnselectAllThumbs(tlist);
				TListDoSelectThumb(
				    tlist, tlist->focus_thumb, NULL, TRUE
				);
				break;
			    }
			}

			if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			    GTK_VISIBILITY_FULL
			)
			    TListMoveTo(tlist, tlist->focus_thumb, 0.0f);

			TListThaw(tlist);
		    }
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Page_Down:
	      case GDK_KP_Page_Down:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    GtkAdjustment *adj = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->hadj : tlist->vadj;
		    if(adj != NULL)
		    {
			adj->value += adj->page_increment;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if(press)
		{
		    gint thumb_len = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->thumb_width : tlist->thumb_height;
		    GtkAdjustment *adj = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->hadj : tlist->vadj;
		    if((adj != NULL) && (thumb_len > 0))
		    {
			gint n = (gint)(adj->page_increment / thumb_len) *
			    tlist->thumbs_per_line;

			TListFreeze(tlist);

			/* Move focus thumb */
			tlist->focus_thumb += n;
			DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

			/* Select thumbs in between */
			if(state & GDK_SHIFT_MASK)
			{
			    switch(tlist->selection_mode)
			    {
			      case GTK_SELECTION_EXTENDED:
			      case GTK_SELECTION_MULTIPLE:
				TListDoSelectThumbRange(
				    tlist, tlist->focus_thumb, NULL 
				);
				break;

			      case GTK_SELECTION_BROWSE:
			      case GTK_SELECTION_SINGLE:
				TListDoUnselectAllThumbs(tlist);
				TListDoSelectThumb(
				    tlist, tlist->focus_thumb, NULL, TRUE
				);
				break;
			    }
			}

			if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			    GTK_VISIBILITY_FULL
			)
			    TListMoveTo(tlist, tlist->focus_thumb, 1.0f);

			TListThaw(tlist);
		    }
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Home:
	      case GDK_KP_Home:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    /* Get adjustment and scroll all the way up */
		    GtkAdjustment *adj = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->hadj : tlist->vadj;
		    if(adj != NULL)
		    {
			adj->value = adj->lower;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if(press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb */
		    tlist->focus_thumb = 0;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    /* Select thumbs in between */
		    if(state & GDK_SHIFT_MASK)
		    {
			switch(tlist->selection_mode)
			{
			  case GTK_SELECTION_EXTENDED:
			  case GTK_SELECTION_MULTIPLE:
			    TListDoSelectThumbRange(
				tlist, tlist->focus_thumb, NULL
			    );
			    break; 

			  case GTK_SELECTION_BROWSE:
			  case GTK_SELECTION_SINGLE:
			    TListDoUnselectAllThumbs(tlist);
			    TListDoSelectThumb(
				tlist, tlist->focus_thumb, NULL, TRUE
			    );
			    break;
			}
		    }

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 0.0f);

		    TListThaw(tlist);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_End:
	      case GDK_KP_End:
		if((state & GDK_CONTROL_MASK) && press)
		{
		    /* Get adjustment and scroll all the way down */
		    GtkAdjustment *adj = (tlist->flags & TLIST_HORIZONTAL) ?
			tlist->hadj : tlist->vadj;
		    if(adj != NULL)
		    {
			adj->value = adj->upper - adj->page_size;
			DO_ADJ_CLAMP_EMIT
		    }
		}
		else if(press)
		{
		    TListFreeze(tlist);

		    /* Move focus thumb */
		    tlist->focus_thumb = tlist->total_thumbs - 1;
		    DO_TLIST_FOCUS_THUMB_CLAMP_DRAW

		    /* Select thumbs in between */
		    if(state & GDK_SHIFT_MASK)
		    {
			switch(tlist->selection_mode)
			{
			  case GTK_SELECTION_EXTENDED:
			  case GTK_SELECTION_MULTIPLE:
			    TListDoSelectThumbRange(
				tlist, tlist->focus_thumb, NULL
			    );
			    break; 

			  case GTK_SELECTION_BROWSE:
			  case GTK_SELECTION_SINGLE:
			    TListDoUnselectAllThumbs(tlist);
			    TListDoSelectThumb(
				tlist, tlist->focus_thumb, NULL, TRUE
			    );
			    break;
			}
		    }

		    if(TListIsThumbVisible(tlist, tlist->focus_thumb) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, tlist->focus_thumb, 1.0f);

		    TListThaw(tlist);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	    }
	}

#undef DO_TLIST_FOCUS_THUMB_CLAMP_DRAW
#undef DO_ADJ_CLAMP_EMIT
#undef DO_STOP_KEY_SIGNAL_EMIT

	return(status);
}

/*
 *	Thumbs List GtkDrawingArea "enter_notify_event" or
 *	"leave_notify_event" signal callback.
 */
static gint TListCrossingEventCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	tlist_struct *tlist = TLIST(data);
	if((widget == NULL) || (crossing == NULL) || (data == NULL))
	    return(FALSE);

	switch((gint)crossing->type)
	{
	  case GDK_ENTER_NOTIFY:
	    break;

	  case GDK_LEAVE_NOTIFY:
	    /* Hide thumb tip */
	    StaticTipSet(widget, NULL, 0, 0, 0, 0);

	    /* Mark that the pointer is not over any thumb */
	    tlist->pointer_over_thumb = -1;

	    TListQueueDraw(tlist);
	    break;
	}

	return(TRUE);
}


/*
 *	Thumbs List GtkDrawingArea "focus_in_event" or "focus_out_event"
 *	signal callback.
 */
static gint TListFocusEventCB(
	GtkWidget *widget, GdkEventFocus *focus, gpointer data
)
{
	tlist_struct *tlist = TLIST(data);
	if((widget == NULL) || (focus == NULL) || (tlist == NULL))
	    return(FALSE);

	if(focus->in && !GTK_WIDGET_HAS_FOCUS(widget))
	{
	    GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
	    TListQueueDraw(TLIST(data));
	}
	else if(!focus->in && GTK_WIDGET_HAS_FOCUS(widget))
	{
	    GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
	    TListQueueDraw(TLIST(data));
	}

	return(TRUE);
}


/*
 *	Horizontal or vertical scroll bar GtkAdjustment "value_changed"
 *	signal callback.
 */
static void TListAdjustmentValueChangedCB(
	GtkAdjustment *adjustment, gpointer data
)
{
	TListQueueDraw(TLIST(data));
}

/*
 *	Returns the pointer to the thumb structure specified by thumb_num
 *	on the given thumbs list. Can return NULL on error.
 */
static tlist_thumb_struct *TListGetThumbPtr(
	tlist_struct *tlist, gint thumb_num
)
{
	if(tlist == NULL)
	    return(NULL);

	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	    return(NULL);
	else
	    return(tlist->thumb[thumb_num]);
}

/*
 *	Returns TRUE if the given thumb is found in the selection list.
 */
static gboolean TListIsThumbSelected(
	tlist_struct *tlist, gint thumb_num
)
{
	GList *glist = (tlist != NULL) ? tlist->selection : NULL;
	if(glist == NULL)
	    return(FALSE);

	while(glist != NULL)
	{
	    if((gint)glist->data == thumb_num)
		return(TRUE);

	    glist = g_list_next(glist);
	}

	return(FALSE);
}


/*
 *	Allocates a new thumb structure.
 */
static tlist_thumb_struct *TListThumbNew(void)
{
	tlist_thumb_struct *thumb = TLIST_THUMB(g_malloc0(
	    sizeof(tlist_thumb_struct)
	));
	if(thumb == NULL)
	    return(NULL);

	thumb->load_state = TLIST_LOAD_STATE_NOT_LOADED;
	thumb->text = NULL;
	thumb->pixmap = NULL;
	thumb->mask = NULL;
	thumb->text_visibility = GTK_VISIBILITY_FULL;
	thumb->loaded_time = 0l;
	thumb->destroy_cb = NULL;
	thumb->data = NULL;

	return(thumb);
}

/*
 *	Delete the Thumb.
 *
 *	Does not call the thumb's destroy notify function.
 */
static void TListThumbDelete(tlist_thumb_struct *thumb)
{
	if(thumb == NULL)
	    return;

	g_free(thumb->text);

	GDK_PIXMAP_UNREF(thumb->pixmap)
	GDK_BITMAP_UNREF(thumb->mask)

	g_free(thumb);
}


/*
 *	Procedure to add the given thumb to the list of selected
 *	thumbs on the thumbs list. If append is FALSE then all previously
 *	selected thumbs will be unselected.
 */
static void TListDoSelectThumb(
	tlist_struct *tlist, gint thumb_num, GdkEventButton *button,
	gboolean append
)
{
	if(tlist == NULL)
	    return;

	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	    return;

	/* Is given thumb already selected? */
	if(g_list_find(tlist->selection, (gpointer)thumb_num) != NULL)
	    return;

	/* If not appending a selection then all selected thumbs must be
	 * unselected first
	 */
	if(!append)
	{
	    GList	*selection = tlist->selection,
			*selection_end = tlist->selection_end;

	    tlist->selection = NULL;
	    tlist->selection_end = NULL;

	    /* Report unselect for all selected thumbs */
	    if(tlist->unselect_cb != NULL)
	    {
		GList *glist = selection_end;
		while(glist != NULL)
		{
		    tlist->unselect_cb(
			(gpointer)tlist,	/* Thumbs List */
			NULL,			/* No GdkButtonEvent */
			(gint)glist->data,	/* Thumb */
			tlist->client_data	/* Data */
		    );
		    glist = glist->prev;
		}
	    }

	    /* Delete entire selection */
	    if(selection != NULL)
		g_list_free(selection);
	}


	/* Append new selected thumb */
	tlist->selection = g_list_append(
	    tlist->selection, (gpointer)thumb_num
	);

	/* Update end selection pointer since main selection changed */
	tlist->selection_end = (tlist->selection != NULL) ?
	    g_list_last(tlist->selection) : NULL;

	/* Report select as needed */
	if(tlist->select_cb != NULL)
	    tlist->select_cb(
		(gpointer)tlist,
		button,
		thumb_num,
		tlist->client_data
	    );

	TListQueueDraw(tlist);
}

/*
 *	Similar to TListDoSelectThumb() except that it selects all
 *	thumbs in sequential indexes from the last selected thumb
 *	to the given thumb_num.
 */
static void TListDoSelectThumbRange(
	tlist_struct *tlist, gint thumb_num, GdkEventButton *button
)
{
	gint i, last_thumb_num;
	GList *glist;


	if(tlist == NULL)
	    return;

	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	    return;

	/* Get last selected thumb if any */
	glist = tlist->selection_end;
	last_thumb_num = (glist != NULL) ? (gint)glist->data : -1;
	if((last_thumb_num < 0) || (last_thumb_num >= tlist->total_thumbs))
	{
	    /* No previously selected thumb, so instead call
	     * TListDoSelectThumb().
	     */
	    TListDoSelectThumb(tlist, thumb_num, button, FALSE);
	    return;
	}

	/* Now select all thumbs from last_thumb_num to thumb_num */
	if(last_thumb_num < thumb_num)
	{
	    /* Select low to high */
	    for(i = last_thumb_num + 1; i <= thumb_num; i++)
		TListDoSelectThumb(tlist, i, button, TRUE);
	}
	else if(last_thumb_num > thumb_num)
	{
	    /* Select high to low */
	    for(i = last_thumb_num - 1; i >= thumb_num; i--)
		TListDoSelectThumb(tlist, i, button, TRUE);
	}
}

/*
 *	Procedure to unselect the given thumb from the list of selected
 *	thumbs on the thumbs list.
 */
static void TListDoUnselectThumb(
	tlist_struct *tlist, gint thumb_num, GdkEventButton *button
)
{
	if(tlist == NULL)
	    return;

	/* Is the specified Thumb not selected? */
	if(g_list_find(tlist->selection, (gpointer)thumb_num) == NULL)
	    return;

	/* Remove the Thumb from the selection list */
	if(tlist->selection != NULL)
	    tlist->selection = g_list_remove(
		tlist->selection, (gpointer)thumb_num
	    );

	/* Update end selection pointer since main selection changed */
	tlist->selection_end = (tlist->selection != NULL) ?
	    g_list_last(tlist->selection) : NULL;

	/* Report unselect as needed */
	if(tlist->unselect_cb != NULL)
	    tlist->unselect_cb(
		(gpointer)tlist,	/* Thumbs List */
		button,			/* GdkButtonEvent */
		thumb_num,		/* Thumb */
		tlist->client_data	/* Data */
	    );

	TListQueueDraw(tlist);
}


/*
 *      Procedure to select all thumbs from the list of selected
 *      thumbs on the thumbs list.
 */
static void TListDoSelectAllThumbs(tlist_struct *tlist)
{
	gint i;

	if(tlist == NULL)
	    return;

	/* Delete entire selection list first */
	if(tlist->selection != NULL)
	    g_list_free(tlist->selection);
	tlist->selection = NULL;
	tlist->selection_end = NULL;


	/* Create selections list for all thumbs */
	for(i = 0; i < tlist->total_thumbs; i++)
	    tlist->selection = g_list_append(
		tlist->selection, (gpointer)i
	    );

	/* Update end selection pointer since main selection changed */
	tlist->selection_end = (tlist->selection != NULL) ?
	    g_list_last(tlist->selection) : NULL;


	/* Report select for all selected thumbs? */
	if(tlist->select_cb != NULL)
	{
	    GList *glist = tlist->selection;
	    while(glist != NULL)
	    {
		tlist->select_cb(
		    (gpointer)tlist,
		    NULL,           /* Do not pass button event */
		    (gint)glist->data,
		    tlist->client_data
		);
		glist = g_list_next(glist);
	    }
	}

	TListQueueDraw(tlist);
}

/*
 *      Procedure to unselect all thumbs from the list of selected
 *      thumbs on the thumbs list.
 */
static void TListDoUnselectAllThumbs(tlist_struct *tlist)
{
	GList *selection, *selection_end;

	if(tlist == NULL)
	    return;

	selection = tlist->selection;
	selection_end = tlist->selection_end;
	tlist->selection = NULL;
	tlist->selection_end = NULL;

	/* Report unselect for all selected thumbs */
	if(tlist->unselect_cb != NULL)
	{
	    GList *glist = selection_end;
	    while(glist != NULL)
	    {
		tlist->unselect_cb(
		    (gpointer)tlist,	/* Thumbs List */
		    NULL,		/* No GdkButtonEvent */
		    (gint)glist->data,	/* Thumb */
		    tlist->client_data	/* Data */
		);
		glist = glist->prev;
	    }
	}

	/* Delete entire selection */
	if(selection != NULL)
	    g_list_free(selection);

	TListQueueDraw(tlist);
}


/*
 *	Returns the Thumb List's preffered thumb pixmap size based
 *	on the given image size.
 */
void TListQueryThumbPixmapSize(
	tlist_struct *tlist,
	gint img_width, gint img_height,
	gint *width, gint *height 
)
{
	gfloat aspect;
	gint	font_height,
		twidth, theight,
		twidth_clip, theight_clip;
	tlist_flags flags;
	GdkFont *font;
	GtkStyle *style;
	GtkWidget *w = (tlist != NULL) ? tlist->list_da : NULL;

	if(width != NULL)
	    *width = 0;
	if(height != NULL)
	    *height = 0;

	if((w == NULL) || (img_width <= 0) || (img_height <= 0))
	    return;

	flags = tlist->flags;
	style = gtk_widget_get_style(w);
	font = (style != NULL) ? style->font : NULL;
	font_height = (font != NULL) ? (font->ascent + font->descent) : 0;

	/* Calculate the exact size of the thumb's pixmap, note that
	 * it will be smaller than the overall thumb's size because
	 * we need to accomidate for the borders, labels, etc
	 */
	if(flags & TLIST_SHOW_THUMB_LABELS)
	{
	    twidth_clip = tlist->thumb_width - (2 * DEF_FRAME_WIDTH) -
		(2 * tlist->thumb_border);
	    theight_clip = tlist->thumb_height - (4 * DEF_FRAME_HEIGHT) -
		(3 * tlist->thumb_border) - font_height;
	}
	else
	{
	    twidth_clip = tlist->thumb_width - (2 * DEF_FRAME_WIDTH) -
		(2 * tlist->thumb_border);
	    theight_clip = tlist->thumb_height - (2 * DEF_FRAME_HEIGHT) -
		(2 * tlist->thumb_border);
	}
	if((twidth_clip <= 0) || (theight_clip <= 0))
	    return;

	/* Begin calculating thumb width and height, mind the aspect
	 * ratio of the given image data
	 */
	aspect = (gfloat)img_width / (gfloat)img_height;

	theight = theight_clip;
	twidth = theight * aspect;

	if(twidth > twidth_clip)
	{
	    aspect = (gfloat)img_height / (gfloat)img_width;

	    twidth = twidth_clip;
	    theight = twidth * aspect;
	}

	if(width != NULL)
	    *width = twidth;
	if(height != NULL)
	    *height = theight;
}

/*
 *	Redraws the drawing area on the given thumbs list.
 *
 *	If the tlist is currently frozen or unmapped then it nothing
 *	will be done.
 */
void TListDraw(tlist_struct *tlist)
{
	gboolean has_focus;
	gint	width, height,
		thumb_width, thumb_height,
		thumb_border, thumb_pixmap_height,
		tpl, font_height;
	tlist_flags flags;
	GdkRectangle text_rect;
	GdkFont *font;
	GdkGC *gc;
	GdkPixmap *bg_pixmap;
	GdkWindow *window;
	GdkDrawable *drawable;
	GtkStateType state;
	GtkStyle *style;
	GtkAdjustment *hadj, *vadj;
	GtkWidget *w = (tlist != NULL) ? tlist->list_da : NULL;
	if(w == NULL)
	    return;

	if((tlist->freeze_count > 0) || !tlist->map_state)
	    return;

	if(!GTK_WIDGET_VISIBLE(w))
	    return;

	flags = tlist->flags;

	/* If the list's pixmap buffer has not been created yet then call
	 * the resize function to create it. Note that the resize function
	 * will call this function back again so we can just return in
	 * this case
	 */
	if((tlist->list_pm == NULL) && (flags & TLIST_DOUBLE_BUFFER))
	{
	    TListResize(tlist, -1, -1);
	    return;
	}

	has_focus = GTK_WIDGET_HAS_FOCUS(w);
	state = GTK_WIDGET_STATE(w);
	style = gtk_widget_get_style(w);
	window = w->window;
	if((style == NULL) || (window == NULL))
	    return;

	font = style->font;
	font_height = (font != NULL) ? (font->ascent + font->descent) : 0;

	gdk_window_get_size(window, &width, &height);
	if((width <= 0) || (height <= 0))
	    return;

	/* Get the pointer to the drawable as the pixmap buffer or the
	 * window itself depending on if we are using double buffer or not
	 */
	if(flags & TLIST_DOUBLE_BUFFER)
	    drawable = (GdkDrawable *)tlist->list_pm;
	else
	    drawable = (GdkDrawable *)window;
	if(drawable == NULL)
	    return;


	/* Create graphic contexts as needed */
	gc = tlist->gc;
	if(gc == NULL)
	{
	    tlist->gc = gc = GDK_GC_NEW();
	    gdk_gc_set_function(gc, GDK_COPY);
	    gdk_gc_set_fill(gc, GDK_SOLID);
	    gdk_gc_set_foreground(gc, &style->bg[state]);
	}
	gc = tlist->selection_gc;
	if(gc == NULL)
	{
	    tlist->selection_gc = gc = GDK_GC_NEW();
	    gdk_gc_set_function(gc, GDK_COPY);
	    gdk_gc_set_fill(gc, GDK_SOLID);
	    gdk_gc_set_foreground(gc, &style->bg[state]);
	}


	gc = tlist->gc;
	bg_pixmap = style->bg_pixmap[state];


	/* Draw background */
	if(bg_pixmap != NULL)
	{
	    gdk_gc_set_fill(gc, GDK_TILED);
	    gdk_gc_set_tile(gc, bg_pixmap);
	    gdk_gc_set_ts_origin(gc, 0, 0);
	}
	else
	{
	    gdk_gc_set_fill(gc, GDK_SOLID);
	    gdk_gc_set_foreground(gc, &style->base[state]);
	}
	gdk_draw_rectangle(
	    drawable, gc, TRUE,
	    0, 0, width, height
	);
	gdk_gc_set_fill(gc, GDK_SOLID);
	gdk_gc_set_tile(gc, NULL);


	/* Begin drawing thumbs */

	hadj = tlist->hadj;
	vadj = tlist->vadj;

	/* Calculate thumbs per line */
	tpl = MAX(tlist->thumbs_per_line, 1);

	/* Get size of each thumb and sizes with in each thumb */
	thumb_width = tlist->thumb_width;
	thumb_height = tlist->thumb_height;
	thumb_border = tlist->thumb_border;
	thumb_pixmap_height = thumb_height - (4 * DEF_FRAME_HEIGHT) -
	    (3 * thumb_border) - font_height;

	/* Calculate bounding box for text rectangle */
	text_rect.x = thumb_border + DEF_FRAME_WIDTH;
	text_rect.y = thumb_height - (3 * DEF_FRAME_HEIGHT) -
	    thumb_border - font_height;
	text_rect.width = MAX(thumb_width - (2 * DEF_FRAME_WIDTH) -
	    (2 * thumb_border), 2);
	text_rect.height = (2 * DEF_FRAME_HEIGHT) + font_height;


	/* Enough information to draw thumbs? */
	if((thumb_width > 0) && (thumb_height > 0) &&
	   (hadj != NULL) && (vadj != NULL)
	)
	{
	    gint i, x, y, i_start, x_start, y_start;
	    tlist_thumb_struct *thumb;

#define DO_DRAW_THUMB_ITERATE	{			\
 if(thumb != NULL) {					\
  gboolean is_selected = TListIsThumbSelected(tlist, i);\
							\
  /* Draw base frame */					\
  if((flags & TLIST_SHOW_THUMB_FRAMES) || is_selected) {\
   if(is_selected)					\
    gdk_gc_set_foreground(				\
     gc, &style->bg[GTK_STATE_SELECTED]			\
    );							\
   else if(tlist->pointer_over_thumb == i)		\
    gdk_gc_set_foreground(				\
     gc, &style->bg[GTK_STATE_PRELIGHT]			\
    );							\
   else							\
    gdk_gc_set_foreground(gc, &style->bg[state]);	\
   gdk_draw_rectangle(					\
    drawable, gc, TRUE,					\
    x, y, thumb_width, thumb_height			\
   );							\
  }							\
							\
  /* Draw pixmap */					\
  if(thumb->pixmap != NULL) {				\
   gint pm_x, pm_y;					\
   gint pm_width, pm_height;				\
   GdkPixmap *pixmap = thumb->pixmap;			\
   GdkBitmap *mask = thumb->mask;			\
							\
   gdk_window_get_size(pixmap, &pm_width, &pm_height);	\
   if(flags & TLIST_SHOW_THUMB_LABELS) {		\
    pm_x = x + (thumb_width / 2) - (pm_width / 2);	\
    pm_y = y + DEF_FRAME_HEIGHT + thumb_border +	\
     (thumb_pixmap_height / 2) -			\
     (pm_height / 2);					\
   } else {						\
    pm_x = x + (thumb_width / 2) - (pm_width / 2);	\
    pm_y = y + (thumb_height / 2) - (pm_height / 2);	\
   }							\
							\
   gdk_gc_set_clip_mask(gc, mask);			\
   gdk_gc_set_clip_origin(gc, pm_x, pm_y);		\
   gdk_draw_pixmap(					\
    drawable, gc, pixmap,				\
    0, 0,						\
    pm_x, pm_y,						\
    pm_width, pm_height					\
   );							\
   gdk_gc_set_clip_mask(gc, NULL);			\
  }							\
							\
  /* Draw text, text base, and text frame */		\
  if((flags & TLIST_SHOW_THUMB_LABELS) &&		\
     (font != NULL) && (thumb->text != NULL)		\
  )							\
  {							\
   const gchar *s = thumb->text;			\
   GdkRectangle rect, text_base_rect;			\
   GdkTextBounds b;					\
							\
   /* Set up translated text rectangle */		\
   rect.x = x + text_rect.x;				\
   rect.y = y + text_rect.y;				\
   rect.width = text_rect.width;			\
   rect.height = text_rect.height;			\
							\
   /* Get geometry of text for the label */		\
   gdk_string_bounds(font, s, &b);			\
							\
   /* Set up to draw text base */			\
   text_base_rect.width = MIN(				\
    (4 * DEF_FRAME_WIDTH) + b.width,			\
    rect.width						\
   );							\
   text_base_rect.height = rect.height;			\
   text_base_rect.x = x + MAX(				\
    (thumb_width / 2) - (text_base_rect.width / 2),	\
    thumb_border + DEF_FRAME_WIDTH			\
   );							\
   text_base_rect.y = rect.y;				\
   gdk_gc_set_foreground(				\
    gc,							\
    is_selected ?					\
     &style->bg[GTK_STATE_SELECTED] :			\
     &style->base[state]				\
   );							\
   /* Draw text base */					\
   if(flags & TLIST_SHOW_THUMB_FRAMES)			\
    gdk_draw_rectangle(					\
     drawable, gc, TRUE,				\
     text_base_rect.x, text_base_rect.y,		\
     text_base_rect.width, text_base_rect.height	\
    );							\
							\
   /* Set up to draw text */				\
   gdk_gc_set_clip_rectangle(gc, &rect);		\
   gdk_gc_set_foreground(				\
    gc,							\
    is_selected ?					\
     &style->text[GTK_STATE_SELECTED] :			\
     &style->text[state]				\
   );							\
   gdk_gc_set_font(gc, font);				\
   /* Draw text */					\
   gdk_draw_string(					\
    drawable, font, gc,					\
    x + ((thumb_width - b.width) / 2),			\
    y + thumb_height - font->descent - thumb_border -	\
     (2 * DEF_FRAME_HEIGHT),				\
    s							\
   );							\
   gdk_gc_set_clip_rectangle(gc, NULL);			\
							\
   /* Draw text frame */				\
   if(flags & TLIST_SHOW_THUMB_FRAMES)			\
    gtk_draw_shadow(					\
     style, drawable, state, GTK_SHADOW_IN,		\
     text_base_rect.x, text_base_rect.y,		\
     text_base_rect.width, text_base_rect.height	\
    );							\
  }							\
							\
  /* Draw frame shadow */				\
  if(flags & TLIST_SHOW_THUMB_FRAMES)			\
   gtk_draw_shadow(					\
    style, drawable, state, GTK_SHADOW_OUT,		\
    x, y, thumb_width, thumb_height			\
   );							\
							\
  /* Draw focus rectangle if the widget is in focus and	\
   * this is the focus_thumb				\
   */							\
  if(has_focus && (tlist->focus_thumb == i)) {		\
   gdk_gc_set_foreground(gc, &style->fg[state]);	\
   if(is_selected)					\
    gdk_gc_set_function(gc, GDK_INVERT);		\
   gdk_draw_rectangle(					\
    drawable, gc, FALSE,				\
    x + 2, y + 2, thumb_width - 5, thumb_height - 5	\
   );							\
   gdk_gc_set_function(gc, GDK_COPY);			\
  }							\
							\
 }							\
}


	    if(tlist->flags & TLIST_HORIZONTAL)
	    {
		/* Calculate starting positions */
		i_start = (gint)(hadj->value / thumb_width) * tpl;
		x_start = -((gint)hadj->value % (gint)thumb_width);
		y_start = -(gint)vadj->value;


		/* Begin iterating and drawing each thumb */
		x = x_start;
		y = y_start;
		for(i = MAX(i_start, 0); i < tlist->total_thumbs; i++)
		{
		    /* Drawn past end of page? */
		    if(x > hadj->page_size)
			break;

		    /* Get this thumb and draw it if it is valid */
		    thumb = tlist->thumb[i];
		    DO_DRAW_THUMB_ITERATE

		    /* Increment coordinates */
		    y += thumb_height;

		    /* Drawn past edge of `row'? */
		    if((y + thumb_height) > vadj->page_size)
		    {
			x += thumb_width;
			y = y_start;
		    }
		}
	    }
	    else
	    {
		/* Calculate starting positions */
		i_start = (gint)(vadj->value / thumb_height) * tpl;
		x_start = -(gint)hadj->value;
		y_start = -((gint)vadj->value % (gint)thumb_height);


		/* Begin iterating and drawing each thumb */
		x = x_start;
		y = y_start;
		for(i = MAX(i_start, 0); i < tlist->total_thumbs; i++)
		{
		    /* Drawn past end of page? */
		    if(y > vadj->page_size)
			break;

		    /* Get this thumb and draw it if it is valid */
		    thumb = tlist->thumb[i];
		    DO_DRAW_THUMB_ITERATE

		    /* Increment coordinates */
		    x += thumb_width;

		    /* Drawn past edge of `row'? */
		    if((x + thumb_width) > hadj->page_size)
		    {
			y += thumb_height;
			x = x_start;
		    }
		}
	    }
	}

#undef DO_DRAW_THUMB_ITERATE

	/* Send drawable to window if drawable is not the window (using
	 * double buffers)
	 */
	if(drawable != window)
	    gdk_draw_pixmap(
		window, gc, drawable,
		0, 0, 0, 0, width, height
	    );
}

/*
 *	Queues a redraw for the Thumbs List.
 */
void TListQueueDraw(tlist_struct *tlist)
{
	gtk_widget_queue_draw((tlist != NULL) ? tlist->list_da : NULL);
}


/*
 *	Resizes the Thumbs List.
 *
 *	The specified width and height may be -1 to indicate to use the
 *	current size.
 *
 *	Recreates the back buffer GdkPixmap if double buffer is
 *	specified.
 *
 *	Recalculates the values for the GtkAdjustments.
 *
 *	A "changed" signal will be emitted to the scroll bars and the
 *	tlist will be redrawn.
 */
void TListResize(tlist_struct *tlist, gint width, gint height)
{
	gint tpl;
	GdkWindow *window;
	GtkAdjustment *hadj, *vadj;
	GtkWidget *w = (tlist != NULL) ? tlist->list_da : NULL;
	if(w == NULL)
	    return;

	window = w->window;
	hadj = tlist->hadj;
	vadj = tlist->vadj;
	if((window == NULL) || (hadj == NULL) || (vadj == NULL))
	    return;

	/* Get current size if it was not specified */
	if((width <= 0) || (height <= 0))
	    gdk_window_get_size(window, &width, &height);

	if((tlist->thumb_width <= 0) || (tlist->thumb_height <= 0) ||
	   (width <= 0) || (height <= 0)
	)
	    return;


	/* Recreate the back buffer GdkPixmap if double buffer is
	 * specified
	 */
	GDK_PIXMAP_UNREF(tlist->list_pm)
	tlist->list_pm = NULL;
	if(tlist->flags & TLIST_DOUBLE_BUFFER)
	{
	    /* Using double buffer, so we need to create a new pixmap
	     * buffer
	     */
	    tlist->list_pm = GDK_PIXMAP_NEW(width, height);
	    if(tlist->list_pm == NULL)
		return;
	}


	/* Begin updating thumbs per line value and scroll adjustments */
	if(tlist->flags & TLIST_HORIZONTAL)
	{
	    /* Calculate new number of thumbs per line */
	    tlist->thumbs_per_line = tpl = MAX(
		height / tlist->thumb_height, 1
	    );

	    /* Update adjustment ranges and page sizes */
	    hadj->lower = 0.0f;
	    hadj->upper = (gfloat)MAX(
		((gint)(tlist->total_thumbs / tpl) * tlist->thumb_width) +
		(((tlist->total_thumbs % tpl) == 0) ? 0 : tlist->thumb_width),
		width
	    );
	    hadj->page_size = (gfloat)width;
	    hadj->page_increment = hadj->page_size / 2;
	    hadj->step_increment = tlist->thumb_width;

	    vadj->lower = 0.0f;
	    vadj->upper = (gfloat)MAX(height, tlist->thumb_height);
	    vadj->page_size = (gfloat)height;
	    vadj->page_increment = vadj->page_size / 2;
	    vadj->step_increment = tlist->thumb_height / 2;
	}
	else
	{
	    /* Calculate new number of thumbs per line */
	    tlist->thumbs_per_line = tpl = MAX(
		width / tlist->thumb_width, 1
	    );

	    /* Update adjustment ranges and page sizes */
	    hadj->lower = 0.0f;
	    hadj->upper = (gfloat)MAX(width, tlist->thumb_width);
	    hadj->page_size = (gfloat)width;
	    hadj->page_increment = hadj->page_size / 2;
	    hadj->step_increment = tlist->thumb_width / 2;

	    vadj->lower = 0.0f;
	    vadj->upper = (gfloat)MAX(
		((gint)(tlist->total_thumbs / tpl) * tlist->thumb_height) +
		(((tlist->total_thumbs % tpl) == 0) ? 0 : tlist->thumb_height),
		height
	    );
	    vadj->page_size = (gfloat)height;
	    vadj->page_increment = vadj->page_size / 2;
	    vadj->step_increment = tlist->thumb_height;
	}

	/* Clip scroll adjustment values */
	if(hadj->value > (hadj->upper - hadj->page_size))
	    hadj->value = hadj->upper - hadj->page_size;
	if(hadj->value < hadj->lower)
	    hadj->value = hadj->lower;

	if(vadj->value > (vadj->upper - vadj->page_size))
	    vadj->value = vadj->upper - vadj->page_size;
	if(vadj->value < vadj->lower)
	    vadj->value = vadj->lower;

	/* Notify adjustments about changes */
	gtk_signal_emit_by_name(GTK_OBJECT(hadj), "changed");
	gtk_signal_emit_by_name(GTK_OBJECT(vadj), "changed");

	gtk_signal_emit_by_name(GTK_OBJECT(hadj), "value_changed");
	gtk_signal_emit_by_name(GTK_OBJECT(vadj), "value_changed");

	/* Show or hide scroll bars depending on adjustment page size */
	w = tlist->hsb;
	if(w != NULL)
	{
	    if((hadj->upper - hadj->lower) > hadj->page_size)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}
	w = tlist->vsb;
	if(w != NULL)
	{
	    if((vadj->upper - vadj->lower) > vadj->page_size)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}
}


/*
 *	Freezes the Thumbs List.
 */
void TListFreeze(tlist_struct *tlist)
{
	if(tlist == NULL)
	    return;

	tlist->freeze_count++;

	/* Previous freeze count was 0? */
	if(tlist->freeze_count == 1)
	{
	    /* Freeze list */


	}
}

/*
 *	Thaws the Thumbs List.
 *
 *	If the previous freeze count was positive then the Thumbs List
 *	will be redrawn.
 */
void TListThaw(tlist_struct *tlist)
{
	if(tlist == NULL)
	    return;

	tlist->freeze_count--;

	/* Queue a redraw when freeze counts reach 0 */
	if(tlist->freeze_count == 0)
	    TListQueueDraw(tlist);

	if(tlist->freeze_count < 0)
	    tlist->freeze_count = 0;
}


/*
 *	Returns the Thumb text visibility based on the specified thumb
 *	size on the Thumbs List.
 */
static GtkVisibility TListThumbTextVisiblity(
	tlist_struct *tlist, const gchar *text
)
{
	GdkTextBounds b;
	GdkFont *font;
	GtkStyle *style;
	GtkWidget *w = (tlist != NULL) ? tlist->list_da : NULL;
	if((w == NULL) || (text == NULL))
	    return(GTK_VISIBILITY_NONE);

	style = gtk_widget_get_style(w);
	font = (style != NULL) ? style->font : NULL;
	if(font == NULL)
	    return(GTK_VISIBILITY_NONE);

	/* Get width of text */
	gdk_string_bounds(font, text, &b);

	/* Is width of text greater than the visible space available
	 * to draw the thumb's text?
	 */
	return(
	    (b.width > (tlist->thumb_width - (2 * tlist->thumb_border)
		- (4 * DEF_FRAME_WIDTH))
	    ) ? GTK_VISIBILITY_PARTIAL : GTK_VISIBILITY_FULL
	);
}

/*
 *	Inserts a new Thumb to the Thumbs List at the position specified
 *	by thumb_num.
 *
 *	Returns the new Thumb's index or -1 on error.
 */
gint TListInsert(tlist_struct *tlist, gint thumb_num, const gchar *text)
{
	tlist_thumb_struct *thumb;

	if(tlist == NULL)
	    return(-1);

	/* Create new Thumb */
	thumb = TListThumbNew();
	if(thumb == NULL)
	    return(-1);

	/* Set text and text visibility */
	thumb->text = STRDUP((text != NULL) ? text : "");
	thumb->text_visibility = TListThumbTextVisiblity(tlist, text);

	/* Append the Thumb if the specified position is out of range */
	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	{
	    /* Append Thumb */
	    thumb_num = MAX(tlist->total_thumbs, 0);
	    tlist->total_thumbs = thumb_num + 1;

	    /* Allocate more pointers */
	    tlist->thumb = (tlist_thumb_struct **)g_realloc(
		tlist->thumb,
		tlist->total_thumbs * sizeof(tlist_thumb_struct *)
	    );
	    if(tlist->thumb == NULL)
	    {
		TListThumbDelete(thumb);
		tlist->total_thumbs = 0;
		return(-1);
	    }              

	    tlist->thumb[thumb_num] = thumb;
	}
	else
	{
	    /* Insert Thumb */
	    gint i;
	    GList *glist;

	    if(tlist->total_thumbs < 0)
		tlist->total_thumbs = 0;
	    tlist->total_thumbs++;

	    /* Allocate more pointers */
	    tlist->thumb = (tlist_thumb_struct **)g_realloc(
		tlist->thumb,
		tlist->total_thumbs * sizeof(tlist_thumb_struct *)
	    );
	    if(tlist->thumb == NULL)
	    {
		TListThumbDelete(thumb);
		tlist->total_thumbs = 0;
		return(-1);
	    }              

	    /* Shift pointers */
	    for(i = tlist->total_thumbs - 1; i > thumb_num; i--)
		tlist->thumb[i] = tlist->thumb[i - 1];

	    tlist->thumb[thumb_num] = thumb;

	    /* Shift selection */
	    for(glist = tlist->selection; glist != NULL; glist = g_list_next(glist))
	    {
		i = (gint)glist->data;
		if(i >= thumb_num)
		    glist->data = (gpointer)(i + 1);
	    }
	}

	TListResize(tlist, -1, -1);

	return(thumb_num);
}

/*
 *	Appends a new Thumb to the Thumbs List.
 *
 *	Returns the new Thumb's index or -1 on error.
 */
gint TListAppend(tlist_struct *tlist, const gchar *text)
{
	return(TListInsert(tlist, -1, text));
}

/*
 *	Sets the load state of the thumb to load_state.
 */
void TListSetLoadState(
	tlist_struct *tlist, gint thumb_num,
	tlist_load_state load_state
)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	if(thumb == NULL)
	    return;

	if(thumb->load_state != load_state)
	{
	    thumb->load_state = load_state;
	    TListQueueDraw(tlist);
	}
}

/*
 *	Sets the text for the thumb specified by thumb_num.
 */
void TListSetText(
	tlist_struct *tlist, gint thumb_num,
	const gchar *text
)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	if(thumb == NULL)
	    return;

	/* Set text and text visibility */
	g_free(thumb->text);
	thumb->text = STRDUP((text != NULL) ? text : "");
	thumb->text_visibility = TListThumbTextVisiblity(tlist, text);

	TListQueueDraw(tlist);
}

/*
 *	Sets the pixmap for the thumb specified by thumb_num.
 */
void TListSetPixmap(
	tlist_struct *tlist, gint thumb_num,
	GdkPixmap *pixmap, GdkBitmap *mask
)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	if(thumb == NULL)
	    return;

	/* If given pixmap is NULL then that implies we should just
	 * unref the currently displayed pixmap
	 */
	if(pixmap == NULL)
	{
	    /* No given pixmap so just unref the existing pixmap and
	     * mask pair (if any)
	     */
	    GDK_PIXMAP_UNREF(thumb->pixmap)
	    GDK_BITMAP_UNREF(thumb->mask)
	}
	else
	{
	    GdkPixmap *oldpixmap = thumb->pixmap;
	    GdkBitmap *oldmask = thumb->mask;

	    /* Increment a refcount on the given pixmap and mask pair,
	     * then set them as the thumb's current pixmap and mask
	     *
	     * If either the given pixmap and/or mask is NULL then the
	     * the thumb's current pixmap and/or mask will be set NULL
	     */
	    gdk_pixmap_ref(pixmap);
	    thumb->pixmap = pixmap;

	    if(mask != NULL)
		gdk_bitmap_ref(mask);
	    thumb->mask = mask;

	    /* Unref thumb's previous pixmap and mask (if any) */
	    GDK_PIXMAP_UNREF(oldpixmap)
	    GDK_BITMAP_UNREF(oldmask)
	}

	TListQueueDraw(tlist);
}

/*
 *	Sets the pixmap image of the thumb specified by thumb_num.
 */
void TListSetRGBA(
	tlist_struct *tlist, gint thumb_num,
	gint width, gint height,        /* Image size in pixels */
	gint bpl,			/* Bytes per line, can be -1 */
	GdkRgbDither dith,
	const guint8 *rgba,
	gboolean no_enlarge		/* Do not upscale if image smaller
					 * than thumb
					 */
)
{
	gint twidth, theight;
	GdkBitmap *mask;
	GdkPixmap *pixmap;
	GtkStyle *style;
	GtkWidget *w;
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	if(thumb == NULL)
	    return;

	/* Unref existing pixmap and mask pair (if any) */
	GDK_PIXMAP_UNREF(thumb->pixmap)
	thumb->pixmap = NULL;
	GDK_BITMAP_UNREF(thumb->mask)
	thumb->mask = NULL;

	/* If no image data is given then stop at this point so that
	 * the only operation done was unloading of the thumb's
	 * previous pixmap
	 */
	if((rgba == NULL) || (width <= 0) || (height <= 0))
	    return;

	/* Calculate bytes per line as needed */
	if(bpl <= 0)
	    bpl = width * 4;

	/* Get values from list's drawing area widget */
	w = tlist->list_da;
	style = gtk_widget_get_style(w);

	/* Calculate the exact size of the pixmap to be displayed
	 * on the thumb based on the specified image size
	 */
	TListQueryThumbPixmapSize(
	    tlist,
	    width, height,		/* Actual size of image */
	    &twidth, &theight		/* Returned thumb size */
	);
	if((twidth <= 0) || (theight <= 0))
	    return;

	/* If the image is not to be enlarged when displayed on the
	 * thumb then update twidth and theight to match the specified
	 * image size
	 */
	if(no_enlarge)
	{
	    if(twidth > width)
		twidth = width;
	    if(theight > height)
		theight = height;
	}

	/* Make sure that the desired thumb size is non-zero */
	if((twidth <= 0) || (theight <= 0))
	    return;

	/* At this point, twidth and theight are the desired size of
	 * the thumb image data
	 */

	/* Do we need to resize the specified image data to
	 * accomidate for the size of the thumb?
	 */
	if((twidth != width) || (theight != height))
	{
	    /* Calculate bytes per line for the thumb image data and
	     * allocate tempory buffer for thumb image
	     */
	    gint tbpl = twidth * 4 * sizeof(guint8);
	    guint8 *thumb_rgba = (guint8 *)g_malloc(tbpl * theight);
	    if(thumb_rgba == NULL)
		return;

	    /* Copy/resize the specified RGBA data to the thumb's
	     * TGBA data
	     */
	    GUIImageBufferResize(
		4,
		rgba, width, height, bpl,
		thumb_rgba, twidth, theight, tbpl
	    );

	    /* Create new thumb pixmap and mask */
	    pixmap = GDK_PIXMAP_NEW(twidth, theight);
	    if(pixmap != NULL)
		gdk_draw_rgb_32_image(
		    pixmap, style->black_gc,
		    0, 0, twidth, theight,
		    dith,
		    (guchar *)thumb_rgba,
		    tbpl
		);
	    mask = GUICreateBitmapFromDataRGBA(
		twidth, theight, tbpl,
		thumb_rgba,
		0x80,			/* Alpha byte threshold */
		NULL
	    );

	    g_free(thumb_rgba);
	}
	else
	{
	    /* Create new thumb pixmap and mask */
	    pixmap = GDK_PIXMAP_NEW(twidth, theight);
	    if(pixmap != NULL) 
		gdk_draw_rgb_32_image(
		    pixmap, style->black_gc,
		    0, 0, twidth, theight,
		    dith,
		    (guchar *)rgba,
		    twidth * 4		/* Bytes per line */
		);
	    mask = GUICreateBitmapFromDataRGBA(
		twidth, theight,
		twidth * 4,		/* Bytes per line */
		rgba,
		0x80,                   /* Alpha byte threshold */
		NULL
	    );
	}

	/* Set new thumb pixmap and mask */
	thumb->pixmap = pixmap;
	thumb->mask = mask;

	TListQueueDraw(tlist);
}

/*
 *	Sets the Thumb's data.
 */
void TListSetThumbData(
	tlist_struct *tlist, gint thumb_num,
	gpointer data   
)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	if(thumb == NULL)
	    return;

	thumb->data = data;
}

/*
 *	Sets the Thumb's data and destroy callback.
 */
void TListSetThumbDataFull(
	tlist_struct *tlist, gint thumb_num,
	gpointer data,
	void (*destroy_cb)(gpointer) 
)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	if(thumb == NULL)
	    return;

	thumb->destroy_cb = destroy_cb;
	thumb->data = data;
}

/*
 *	Deletes the specified Thumb on the Thumbs List.
 */
void TListRemove(tlist_struct *tlist, gint thumb_num)
{
	gint i;
	GList *glist;
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	if(thumb == NULL)
	    return;

	/* Unselect Thumb before deleting */
	TListDoUnselectThumb(tlist, thumb_num, NULL);

	/* Notify about the Thumb being deleted */
	if(thumb->destroy_cb != NULL)
	    thumb->destroy_cb(thumb->data);

	/* Need to shift selection data value referencing any thumb index
	 * who's value is greater than this thumb since this thumb is
	 * about to be deleted
	 */
	for(glist = tlist->selection; glist != NULL; glist = g_list_next(glist))
	{
	    i = (gint)glist->data;
	    if(i > thumb_num)
		glist->data = (gpointer)(i - 1);
	}

	/* Delete Thumb */
	TListThumbDelete(thumb);
	tlist->thumb[thumb_num] = thumb = NULL;

	/* Reduce pointers on thumbs list and shift as needed */
	tlist->total_thumbs--;
	if(tlist->total_thumbs > 0)
	{
	    /* Shift pointers */
	    for(i = thumb_num; i < tlist->total_thumbs; i++)
		tlist->thumb[i] = tlist->thumb[i + 1];

	    /* Reduce pointer array allocation */
	    tlist->thumb = (tlist_thumb_struct **)g_realloc(
		tlist->thumb,
		tlist->total_thumbs * sizeof(tlist_thumb_struct *)
	    );
	    if(tlist->thumb == NULL)
	    {
		tlist->total_thumbs = 0;
	    }
	}
	else
	{
	    /* Total has reached 0, Delete pointer array */
	    g_free(tlist->thumb);
	    tlist->thumb = NULL;
	    tlist->total_thumbs = 0;
	}

	/* Clamp focus thumb */
	if(tlist->focus_thumb >= tlist->total_thumbs)
	    tlist->focus_thumb = tlist->total_thumbs - 1;
	if(tlist->focus_thumb < 0)
	    tlist->focus_thumb = 0;

	TListResize(tlist, -1, -1);
}

/*
 *	Deletes all Thumbs on the Thumbs List.
 */
void TListClear(tlist_struct *tlist)
{
	gint i;
	GList *glist, *selection, *selection_end;
	tlist_thumb_struct *thumb;

	if(tlist == NULL)
	    return;

	/* Transfer selection locally */
	selection = tlist->selection;
	selection_end = tlist->selection_end;
	tlist->selection = NULL;
	tlist->selection_end = NULL;

	/* Report unselect for all selected thumbs */
	if(tlist->unselect_cb != NULL)
	{
	    for(glist = selection_end; glist != NULL; glist = g_list_previous(glist))
		tlist->unselect_cb(
		    tlist,		/* Thumbs List */
		    NULL,		/* No GtkButtonEvent */
		    (gint)glist->data,	/* Thumb */
		    tlist->client_data	/* Data */
		);
	}
	/* Delete selection */
	if(selection != NULL)
	    g_list_free(selection);

	/* Reset focus and pointer over thumbs */
	tlist->focus_thumb = 0;
	tlist->pointer_over_thumb = -1;

	/* Notify about each Thumb being deleted and delete each Thumb */
	for(i = tlist->total_thumbs - 1; i >= 0; i--)
	{
	    thumb = tlist->thumb[i];
	    if(thumb == NULL)
		continue;

	    /* Notify about this Thumb being deleted */
	    if(thumb->destroy_cb != NULL)
		thumb->destroy_cb(thumb->data);

	    /* Delete this Thumb */
	    TListThumbDelete(thumb);
	    tlist->thumb[i] = thumb = NULL;
	}

	/* Delete pointer array */
	g_free(tlist->thumb);
	tlist->thumb = NULL;
	tlist->total_thumbs = 0;

	TListResize(tlist, -1, -1);
}


/*
 *	Returns the Thumb's load state.
 */
tlist_load_state TListGetLoadState(tlist_struct *tlist, gint thumb_num)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	return((thumb != NULL) ? thumb->load_state : TLIST_LOAD_STATE_NOT_LOADED);
}

/*
 *	Returns the Thumb's text.
 *
 *	Returns TRUE if the Thumb is valid and the value is returned.
 */
gboolean TListGetText(
	tlist_struct *tlist, gint thumb_num, gchar **text
)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);

	if(text != NULL)
	    *text = NULL;

	if(thumb == NULL)
	    return(FALSE);

	if(text != NULL)
	    *text = thumb->text;

	return(TRUE);
}

/*
 *	Returns the Thumb's pixmap.
 *
 *	Returns TRUE if the Thumb is valid and the value is returned.
 */
gboolean TListGetPixmap(
	tlist_struct *tlist, gint thumb_num,
	GdkPixmap **pixmap, GdkBitmap **mask
)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);

	if(pixmap != NULL)
	    *pixmap = NULL;
	if(mask != NULL)
	    *mask = NULL;

	if(thumb == NULL)
	    return(FALSE);

	if(pixmap != NULL)
	    *pixmap = thumb->pixmap;
	if(mask != NULL)
	    *mask = thumb->mask;

	return(TRUE);
}

/*
 *	Returns the Thumb's data.
 */
gpointer TListGetThumbData(tlist_struct *tlist, gint thumb_num)
{
	tlist_thumb_struct *thumb = TListGetThumbPtr(tlist, thumb_num);
	return((thumb != NULL) ? thumb->data : NULL);
}


/*
 *	Selects the thumb specified by thumb_num.
 *
 *	If the thumb is already selected or does not exist then nothing
 *	will be done.
 */
void TListSelectThumb(tlist_struct *tlist, gint thumb_num)
{
	TListDoSelectThumb(tlist, thumb_num, NULL, TRUE);
}

/*
 *      Unselects the thumb specified by thumb_num.
 *
 *	If the thumb is not selected or does not exist then nothing will
 *	be done.
 */
void TListUnselectThumb(tlist_struct *tlist, gint thumb_num)
{
	TListDoUnselectThumb(tlist, thumb_num, NULL);
}

/*
 *	Selects all thumbs.
 */
void TListSelectAll(tlist_struct *tlist)
{
	TListDoSelectAllThumbs(tlist);
}

/*
 *	Unselects all thumbs.
 */
void TListUnselectAll(tlist_struct *tlist)
{
	TListDoUnselectAllThumbs(tlist);
}


/*
 *	Returns the thumb number, and its x and y index position based
 *	on the given x and y coordinates relative to the thumb's list
 *	drawing area widget.
 *
 *	Returns TRUE if a selection is made, or FALSE on no match.
 */
gboolean TListGetSelection(
	tlist_struct *tlist, gint x, gint y,
	gint *thumb_num, gint *thumb_ix, gint *thumb_iy
)
{
	gint i, ix, iy, tpl, thumb_width, thumb_height;
	GtkAdjustment *hadj, *vadj;

	if(thumb_num != NULL)
	    *thumb_num = -1;
	if(thumb_ix != NULL)
	    *thumb_ix = 0;
	if(thumb_iy != NULL)
	    *thumb_iy = 0;

	if(tlist == NULL)
	    return(FALSE);

	tpl = MAX(tlist->thumbs_per_line, 1);
	thumb_width = tlist->thumb_width;
	thumb_height = tlist->thumb_height;
	hadj = tlist->hadj;
	vadj = tlist->vadj;
	if((hadj == NULL) || (vadj == NULL) ||
	   (thumb_width <= 0) || (thumb_height <= 0)
	)
	    return(FALSE);

	/* Calculate thumb index coordinates */
	ix = (gint)(x + hadj->value) / thumb_width;
	iy = (gint)(y + vadj->value) / thumb_height;
	if(thumb_ix != NULL)
	    *thumb_ix = ix;
	if(thumb_iy != NULL)
	    *thumb_iy = iy;

	/* Calculate thumb index */
	if(tlist->flags & TLIST_HORIZONTAL)
	{
	    if((y >= 0) && (y < (tpl * thumb_height)))
		i = (ix * tpl) + CLIP(iy, 0, tpl - 1);
	    else
		i = -1;
	}
	else
	{
	    if((x >= 0) && (x < (tpl * thumb_width)))
		i = (iy * tpl) + CLIP(ix, 0, tpl - 1);
	    else
		i = -1;
	}

	/* Thumb index out of range? */
	if((i < 0) || (i >= tlist->total_thumbs))
	    return(FALSE);

	if(thumb_num != NULL)
	    *thumb_num = i;

	return(TRUE);
}

/*
 *	Returns the coordinate position of the specified thumb relative
 *	to the Thumbs List GtkDrawingArea (with scroll adjustments
 *	applied).
 *
 *	Returns TRUE if thumb_num is valid and a coordinate position is
 *	given. The returned thumb coordinate position may or may not be
 *	visible.
 */
gboolean TListGetPosition(
	tlist_struct *tlist, gint thumb_num,
	gint *x, gint *y
)
{
	gint lx, ly, tpl;
	GtkAdjustment *hadj, *vadj;


	if(x != NULL)
	    *x = 0;
	if(y != NULL)
	    *y = 0;

	if(tlist == NULL)
	    return(FALSE);

	/* Non-existant thumb index? */
	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	    return(FALSE);

	/* Get thumbs per line and scroll adjustments */
	tpl = MAX(tlist->thumbs_per_line, 1);
	hadj = tlist->hadj;
	vadj = tlist->vadj;
	if((hadj == NULL) || (vadj == NULL))
	    return(FALSE);

	if((tlist->thumb_width <= 0) || (tlist->thumb_height <= 0))
	    return(FALSE);

	if(tlist->flags & TLIST_HORIZONTAL)
	{
	    lx = (gint)(thumb_num / tpl) * tlist->thumb_width;
	    ly = (thumb_num % tpl) * tlist->thumb_height;
	}
	else
	{
	    lx = (thumb_num % tpl) * tlist->thumb_width;
	    ly = (gint)(thumb_num / tpl) * tlist->thumb_height;
	}
	if(x != NULL)
	    *x = (gint)(lx - hadj->value);
	if(y != NULL)
	    *y = (gint)(ly - vadj->value);

	return(TRUE);
}


/*
 *	Returns the visibility of the specified thumb.
 */
GtkVisibility TListIsThumbVisible(tlist_struct *tlist, gint thumb_num)
{
	gint x, y, tpl;
	GtkAdjustment *hadj, *vadj;


	if(tlist == NULL)
	    return(GTK_VISIBILITY_NONE);

	/* Non-existant thumb index? */
	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	    return(GTK_VISIBILITY_NONE);

	/* Get thumbs per line and scroll adjustments */
	tpl = MAX(tlist->thumbs_per_line, 1);
	hadj = tlist->hadj;
	vadj = tlist->vadj;
	if((hadj == NULL) || (vadj == NULL))
	    return(GTK_VISIBILITY_NONE);

	if((tlist->thumb_width <= 0) || (tlist->thumb_height <= 0))
	    return(GTK_VISIBILITY_NONE);

	/* Calculate coordinates of thumb (without scroll adjustments
	 * applied)
	 */
	if(tlist->flags & TLIST_HORIZONTAL)
	{
	    x = (gint)(thumb_num / tpl) * tlist->thumb_width;
	    y = (thumb_num % tpl) * tlist->thumb_height;
	}
	else
	{
	    x = (thumb_num % tpl) * tlist->thumb_width;
	    y = (gint)(thumb_num / tpl) * tlist->thumb_height;
	}

	/* Check if the thumb coordinate position visiblity by masking
	 * the absolute coordinate values of the adjustments to them
	 */

	/* Totally obscured? */
	if(((gint)(x + tlist->thumb_width) < (gint)hadj->value) ||
	   ((gint)x >= (gint)(hadj->value + hadj->page_size)) ||
	   ((gint)(y + tlist->thumb_height) < (gint)vadj->value) ||
	   ((gint)y >= (gint)(vadj->value + vadj->page_size))
	)
	    return(GTK_VISIBILITY_NONE);
	/* Fully visible? */
	else if(((gint)x >= (gint)hadj->value) &&
		((gint)(x + tlist->thumb_width) <=
		    (gint)(hadj->value + hadj->page_size)) &&
		((gint)y >= (gint)vadj->value) &&
		((gint)(y + tlist->thumb_height) <=
		    (gint)(vadj->value + vadj->page_size))
	)
	    return(GTK_VISIBILITY_FULL);
	else
	    return(GTK_VISIBILITY_PARTIAL);
}

/* 
 *	Scrolls the Thumb List to the specified thumb and offsets
 *	relative to the specified coefficient.
 */
void TListMoveTo(
	tlist_struct *tlist, gint thumb_num, gfloat coeff
)
{
	gint tpl;
	GtkAdjustment *hadj, *vadj;

	if(tlist == NULL)
	    return;

	/* Non-existant thumb index? */
	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	    return;

	/* Sanitize coefficient */
	if(coeff < 0.0f)
	    coeff = 0.0f;
	if(coeff > 1.0f)
	    coeff = 1.0f;

	/* Get thumbs per line and scroll adjustments */
	tpl = MAX(tlist->thumbs_per_line, 1);
	hadj = tlist->hadj;
	vadj = tlist->vadj;
	if((hadj == NULL) || (vadj == NULL))
	    return;

	/* Calculate position of thumb, apply coefficient offset, and
	 * scroll to the new position
	 */
	if(tlist->flags & TLIST_HORIZONTAL)
	{
	    /* Calculate position of thumb */
	    gint x = (gint)(thumb_num / tpl) * tlist->thumb_width;

	    /* Apply coefficient offset */
	    x = x - (coeff * (hadj->page_size - tlist->thumb_width));

	    /* Sanitize */
	    if((gfloat)x > (hadj->upper - hadj->page_size))
		x = (gint)(hadj->upper - hadj->page_size);
	    if((gfloat)x < hadj->lower)
		x = (gint)hadj->lower;

	    gtk_adjustment_set_value(hadj, (gfloat)x);
	}
	else
	{
	    /* Calculate position of thumb */
	    gint y = (gint)(thumb_num / tpl) * tlist->thumb_height;

	    /* Apply coefficient offset */
	    y = y - (coeff * (vadj->page_size - tlist->thumb_height));

	    /* Sanitize */
	    if((gfloat)y > (vadj->upper - vadj->page_size))
		y = (gint)(vadj->upper - vadj->page_size);
	    if((gfloat)y < vadj->lower)
		y = (gint)vadj->lower;

	    gtk_adjustment_set_value(vadj, (gfloat)y);
	}
}


/*
 *	Creates a new Thumbs List.
 */
tlist_struct *TListNew(
	gboolean horizontal,
	gint thumb_width, gint thumb_height, gint thumb_border,
	gpointer client_data,
	void (*select_cb)(gpointer, GdkEventButton *, gint, gpointer),
	void (*unselect_cb)(gpointer, GdkEventButton *, gint, gpointer)
)
{
	const gint border_minor = 3;
	GtkAdjustment *adj;
	GtkWidget *w, *parent, *parent2;
	tlist_struct *tlist = TLIST(g_malloc0(
	    sizeof(tlist_struct)
	));
	if(tlist == NULL)
	    return(tlist);

	/* Reset values */
	tlist->map_state = FALSE;
	tlist->freeze_count = 0;
	tlist->flags = (horizontal ? TLIST_HORIZONTAL : 0) |
	    TLIST_DOUBLE_BUFFER |
	    TLIST_SHOW_THUMB_FRAMES |
	    TLIST_SHOW_THUMB_LABELS;

	tlist->gc = NULL;
	tlist->selection_gc = NULL;

	tlist->focus_thumb = 0;
	tlist->pointer_over_thumb = -1;
	tlist->selection_mode = GTK_SELECTION_SINGLE;
	tlist->selection = NULL;
	tlist->selection_end = NULL;

	tlist->thumb = NULL;
	tlist->total_thumbs = 0;

	tlist->thumb_width = thumb_width;
	tlist->thumb_height = thumb_height;
	tlist->thumb_border = thumb_border;
	tlist->thumbs_per_line = 1;

	tlist->client_data = client_data;
	tlist->select_cb = select_cb;
	tlist->unselect_cb = unselect_cb;


	/* Create toplevel as a table */
	tlist->toplevel = w = gtk_table_new(2, 2, FALSE);
	gtk_table_set_row_spacings(GTK_TABLE(w), border_minor);
	gtk_table_set_col_spacings(GTK_TABLE(w), border_minor);
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	parent = w;


	/* Frame that will hold drawing area */
	w = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_table_attach(
	    GTK_TABLE(parent), w,
	    0, 1,
	    0, 1,
	    GTK_SHRINK | GTK_FILL | GTK_EXPAND,
	    GTK_SHRINK | GTK_FILL | GTK_EXPAND,
	    0, 0
	);
	gtk_widget_show(w);
	parent2 = w;

	/* List drawing area */
	tlist->list_da = w = gtk_drawing_area_new();
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT | GTK_CAN_FOCUS);
	gtk_widget_add_events(
	    w,
	    GDK_VISIBILITY_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK |
	    GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK |
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
	    GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
	    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
	    GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
	);
#if 0
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "draw",
	    GTK_SIGNAL_FUNC(TListDrawCB), tlist
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "draw_focus",
	    GTK_SIGNAL_FUNC(TListDrawFocusCB), tlist
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "draw_default",
	    GTK_SIGNAL_FUNC(TListDrawDefaultCB), tlist
	);
#endif
	gtk_signal_connect(
	    GTK_OBJECT(w), "configure_event",
	    GTK_SIGNAL_FUNC(TListConfigureEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(TListExposeEventCB), tlist
	);                                            
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(TListKeyEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(TListKeyEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(TListButtonEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(TListButtonEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "motion_notify_event",
	    GTK_SIGNAL_FUNC(TListMotionEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(TListCrossingEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(TListCrossingEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_in_event",
	    GTK_SIGNAL_FUNC(TListFocusEventCB), tlist
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_out_event",
	    GTK_SIGNAL_FUNC(TListFocusEventCB), tlist
	);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);


	/* Vertical adjustment and scroll bar */
	tlist->vadj = adj = (GtkAdjustment *)gtk_adjustment_new(
	    0.0, 0.0, 10.0, 10.0, 10.0, 10.0
	);
	gtk_object_ref(GTK_OBJECT(adj));
	tlist->vsb = w = gtk_vscrollbar_new(adj);
	gtk_table_attach(
	    GTK_TABLE(parent), w,
	    1, 2,
	    0, 1,
	    GTK_FILL,
	    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
	    0, 0
	);
	gtk_signal_connect(
	    GTK_OBJECT(adj), "value_changed",
	    GTK_SIGNAL_FUNC(TListAdjustmentValueChangedCB), tlist
	);
	/* Do not show this now, it will be mapped/unmapped when resizing
	 * of list drawing area occures
	 */

	/* Horizontal adjustment and scroll bar */
	tlist->hadj = adj = (GtkAdjustment *)gtk_adjustment_new(
	    0.0, 0.0, 10.0, 10.0, 10.0, 10.0
	);
	gtk_object_ref(GTK_OBJECT(adj));
	tlist->hsb = w = gtk_hscrollbar_new(adj);
	gtk_table_attach(
	    GTK_TABLE(parent), w,
	    0, 1,
	    1, 2,
	    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
	    GTK_FILL,
	    0, 0
	);
	gtk_signal_connect(
	    GTK_OBJECT(adj), "value_changed",
	    GTK_SIGNAL_FUNC(TListAdjustmentValueChangedCB), tlist
	);
	/* Do not show this now, it will be mapped/unmapped when resizing
	 * of list drawing area occures
	 */



	return(tlist);
}

/*
 *	Sets the Thumb's List thumb geometry.
 */
void TListThumbGeometry(
	tlist_struct *tlist, 
	gint thumb_width, gint thumb_height, gint thumb_border
)
{
	if(tlist == NULL)  
	    return;

	if((tlist->thumb_width != thumb_width) ||
	   (tlist->thumb_height != thumb_height) ||
	   (tlist->thumb_border != thumb_border)
	)
	{
	    tlist->thumb_width = thumb_width;
	    tlist->thumb_height = thumb_height;
	    tlist->thumb_border = thumb_border;

	    TListQueueDraw(tlist);
	}
}

/*
 *	Sets the Thumbs List selection mode.
 */
void TListSelectionMode(
	tlist_struct *tlist, GtkSelectionMode selection_mode
)
{
	if(tlist == NULL)
	    return;

	tlist->selection_mode = selection_mode;
}

/*
 *	Sets the Thumbs List in double buffer or single buffer mode.
 */
void TListDoubleBuffer(tlist_struct *tlist, gboolean double_buffer)
{
	gboolean was_double_buffer;

	if(tlist == NULL)
	    return;

	was_double_buffer = (tlist->flags & TLIST_DOUBLE_BUFFER) ? TRUE : FALSE;
	if(was_double_buffer != double_buffer)
	{
	    if(double_buffer)
	    {
		/* Switching to double buffer */
		tlist->flags |= TLIST_DOUBLE_BUFFER;

		/* Call resize function to detect change to double
		 * buffer mode and recreate the list_pm
		 */
		TListResize(tlist, -1, -1);
	    }
	    else
	    {
		/* Switching to single buffer */
		tlist->flags &= ~TLIST_DOUBLE_BUFFER;

		/* Get rid of the list's back buffer (if any) */
		GDK_PIXMAP_UNREF(tlist->list_pm)
		tlist->list_pm = NULL;
	    }
	}
}

/*
 *	Sets the Thumbs List orientation.
 */
void TListOrientation(tlist_struct *tlist, gboolean horizontal)
{
	gboolean was_horizontal;

	if(tlist == NULL)
	    return;

	was_horizontal = (tlist->flags & TLIST_HORIZONTAL) ? TRUE : FALSE;

	if(was_horizontal != horizontal)
	{
	    GtkAdjustment	*hadj = tlist->hadj,
				*vadj = tlist->vadj;

	    /* Set new orientation */
	    if(horizontal)
		tlist->flags |= TLIST_HORIZONTAL;
	    else
		tlist->flags &= ~TLIST_HORIZONTAL;

	    /* Swap scroll adjustment values */
	    if((hadj != NULL) && (vadj != NULL))
	    {
		gfloat v = hadj->value;
		hadj->value = vadj->value;
		vadj->value = v;
	    }

	    /* Call resize function to detect change to horizontal or
	     * vertical mode and recreate the list_pm
	     */
	    TListResize(tlist, -1, -1);
	}
}

/*
 *	Sets the Thumbs List to show or hide thumb frames.
 */
void TListShowThumbFrames(tlist_struct *tlist, gboolean show)
{
	gboolean was_show;

	if(tlist == NULL)
	    return;

	was_show = (tlist->flags & TLIST_SHOW_THUMB_FRAMES) ? TRUE : FALSE;

	if(was_show != show)
	{
	    if(show)
		tlist->flags |= TLIST_SHOW_THUMB_FRAMES;
	    else
		tlist->flags &= ~TLIST_SHOW_THUMB_FRAMES;

	    TListQueueDraw(tlist);
	}
}

/*
 *	Sets the Thumbs List to show or hide thumb labels.
 */
void TListShowThumbLabels(tlist_struct *tlist, gboolean show)
{
	gboolean was_show;

	if(tlist == NULL)
	    return;
		   
	was_show = (tlist->flags & TLIST_SHOW_THUMB_LABELS) ? TRUE : FALSE;

	if(was_show != show)
	{
	    if(show)
		tlist->flags |= TLIST_SHOW_THUMB_LABELS;
	    else
		tlist->flags &= ~TLIST_SHOW_THUMB_LABELS;

	    TListQueueDraw(tlist);
	}
}

/*
 *	Sets the Thumbs List to show or not show texttips for obscured
 *	thumb labels.
 */
void TListShowTextTips(tlist_struct *tlist, gboolean show)
{
	gboolean was_show;

	if(tlist == NULL)
	    return;
		   
	was_show = (tlist->flags & TLIST_SHOW_TEXTTIPS) ? TRUE : FALSE;

	if(was_show != show)
	{
	    if(show)
		tlist->flags |= TLIST_SHOW_TEXTTIPS;
	    else                                      
		tlist->flags &= ~TLIST_SHOW_TEXTTIPS;

	    TListQueueDraw(tlist);
	}
}


/*
 *	Maps the Thumbs List.
 */
void TListMap(tlist_struct *tlist)
{
	GtkWidget *w = (tlist != NULL) ? tlist->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_show(w);
	tlist->map_state = TRUE;
}

/*
 *	Unmaps the Thumbs List.
 */
void TListUnmap(tlist_struct *tlist)
{
	GtkWidget *w = (tlist != NULL) ? tlist->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_hide(w);
	tlist->map_state = FALSE;
}

/*
 *	Delete the Thumbs List.
 */
void TListDelete(tlist_struct *tlist)
{
	if(tlist == NULL)
	    return;

	/* Unselect and delete all thumbs */
	TListClear(tlist);

	tlist->freeze_count++;

	/* Begin destroying widgets */
	GTK_WIDGET_DESTROY(tlist->list_da)
	GTK_WIDGET_DESTROY(tlist->toplevel)

	GDK_PIXMAP_UNREF(tlist->list_pm)

	GTK_OBJECT_UNREF(tlist->vadj)
	GTK_OBJECT_UNREF(tlist->hadj)

	GDK_GC_UNREF(tlist->gc)
	GDK_GC_UNREF(tlist->selection_gc)

	tlist->freeze_count--;

	g_free(tlist);
}
