#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <gtk/gtk.h>
#include <gtk/gtkinvisible.h>
#include <gdk/gdkkeysyms.h>
#if defined(_WIN32)
# include <gdk/gdkprivate.h>
# include <gdk/gdkwin32.h>
#else
# include <gdk/gdkx.h>
#endif

#if defined(__SOLARIS__) || defined(__hpux)
# include "../include/os.h"
#endif

#include "guiutils.h"


#include "images/icon_folder_opened_20x20.xpm"
#include "images/icon_cut_20x20.xpm"
#include "images/icon_copy_20x20.xpm"
#include "images/icon_paste_20x20.xpm"
#include "images/icon_cancel_20x20.xpm"


/*
 *	Root GdkWindow:
 */
static GdkWindow	*gui_window_root = NULL;


/*
 *	Block Input Data:
 *
 *	For GUIBlockInput*().
 */
typedef struct {
	GtkWidget	*invisible;		/* To grab (steal) GdkEvents
											 * so that the blocked
											 * GtkWidget does not get them  */
	GtkWidget	*w;			/* The GtkWidget being blocked */
	gint		level;
} GUIBlockInputData;
#define GUI_BLOCK_INPUT_DATA(p)		((GUIBlockInputData *)(p))
#define GUI_BLOCK_INPUT_DATA_KEY	"GUI/BlockInput"


/*
 *	Selection Data:
 */
typedef struct {
	GdkAtom		selection_atom,
					target_atom,
					type_atom;
	gpointer	data;			/* Data (coppied) */
	gint		length;			/* Data length in bytes */
} GUISelectionData;
#define GUI_SELECTION_DATA(p)		((GUISelectionData *)(p))
#define GUI_SELECTION_DATA_KEY		"GUI/Selection/Data"

/*
 *	Selection Owner:
 */
typedef struct {
	GtkWidget	*w;			/* Owner GtkWidget */
	guint		selection_clear_event_sigid,
					selection_get_sigid;
	GList		*datas_list;		/* GList of GUISelectionData * */
} GUISelectionOwner;
#define GUI_SELECTION_OWNER(p)		((GUISelectionOwner *)(p))
#define GUI_SELECTION_OWNER_KEY		"GUI/Selection/Owner"


/*
 *	DND Source GtkWidget Data:
 */
typedef struct {
	GtkWidget	*widget;		/* This source GtkWidget */
	GList		*targets_list;		/* GList of GtkTargetEntry *
											 * targets */
} GUIDNDSourceData;
#define GUI_DND_SOURCE_DATA(p)		((GUIDNDSourceData *)(p))
#define GUI_DND_SOURCE_DATA_KEY		"GUI/DND/Source"

/*
 *	GList of GtkWidget * DND source widgets.
 */
static GList	*gui_dnd_source_widgets_list = NULL;


/*
 *	DND Target GtkWidget Data:
 */
typedef enum {
	GUI_DND_TARGET_HIGHLIGHT	= (1 << 0),	/* Do highlighting of
													 * the target GtkWidget */
	GUI_DND_TARGET_HIGHLIGHTED	= (1 << 1),	/* Currently highlighted */
	GUI_DND_TARGET_DRAG_OVER	= (1 << 2)	/* Drag is currently
													 * over the target
													 * GtkWidget (not
													 * nessisarily accepted) */
} GUIDNDTargetFlags;

typedef struct {
	GUIDNDTargetFlags	flags;
	GtkWidget	*widget;		/* This target GtkWidget */
	GList		*targets_list;		/* GList of GtkTargetEntry *
											 * targets */
	GdkDragAction	allowed_actions,	/* List of allowed actions */
					default_action_same,	/* Action to use if same widget */
					default_action;		/* Action for all other cases */
	gint		x, y;			/* Current drag position over
											 * the widget */
	guint		highlight_draw_sigid,
					unhighlight_draw_sigid,
					drag_leave_sigid,
					drag_motion_sigid;
} GUIDNDTargetData;
#define GUI_DND_TARGET_DATA(p)		((GUIDNDTargetData *)(p))
#define GUI_DND_TARGET_DATA_KEY		"GUI/DND/Target"


/*
 *	DND Drag Icon:
 *
 *	Initialized on the first call to GUIDNDSetDragIcon() and used
 *	in GUIDNDDragBeginCB().
 */
typedef struct {
	GtkWidget	*toplevel;		/* GtkWindow */
	gint		ref_count;
	GtkWidget	*icon_pm;		/* Icon GtkPixmap */
	gint		x,			/* Hot spot */
					y,
					width,			/* Size */
					height;
} GUIDNDDragIcon;
#define GUI_DND_DRAG_ICON(p)		((GUIDNDDragIcon *)(p))
#define GUI_DND_DRAG_ICON_KEY		"GUI/DND/DragIcon"


/*
 *	Banner Data:
 */
typedef struct {
	GtkWidget	*toplevel;	/* GtkDrawingArea */
	gchar		*label;
	GdkFont		*font;
	GdkColormap	*colormap;
	GdkColor	*color_fg,
					*color_bg,
					*color_border;
	GtkJustification	justify;
	gint		inside_border,
					outside_border;
	GdkGC		*gc;
} GUIBannerData;
#define GUI_BANNER_DATA(p)		((GUIBannerData *)(p))
#define GUI_BANNER_DATA_KEY		"GUI/Banner"


/*
 *	Button Data:
 *
 *	For recording widgets on buttons and toggle buttons, stored as
 *	the object's user data.
 */
typedef struct {
	GtkWidget	*button,	/* GtkButton */
					*main_box,	/* GtkBox */
					*label,		/* GtkLabel */
					*icons_toplevel,/* GtkBox */
					*pixmap,	/* GtkPixmap */
					*arrow;		/* GtkArrow */
	gchar		*label_text;	/* Copy of label text */
	const guint8	**icon_data;	/* Shared icon data */
	GtkArrowType	arrow_type;
} GUIButtonData;
#define GUI_BUTTON_DATA(p)		((GUIButtonData *)(p))
#define GUI_BUTTON_DATA_KEY		"GUI/Button"

/*
 *	Pullout Data:
 */
#define GUI_PULLOUT_DATA_KEY		"GUI/PullOut"

/*
 *	Combo Data:
 */
#define GUI_COMBO_DATA_KEY		"GUI/Combo"


/*
 *	Menu Item Data:
 *
 *	For recording widgets on menu items, stored as the object's
 *	user data.
 */
typedef struct {
	GtkWidget	*menu_item,
					*pixmap,		/* Icon GtkPixmap */
					*label,			/* Main GtkLabel */
					*accel_label;		/* Accelkey GtkLabel */
	gchar		*label_text,		/* Copy of the label text */
					*accel_text;		/* Copy of accelkey label text */
	const guint8	**icon_data;		/* Shared icon data for the pixmap */
	guint		accel_key,
					accel_mods;
} GUIMenuItemData;
#define GUI_MENU_ITEM_DATA(p)		((GUIMenuItemData *)(p))
#define GUI_MENU_ITEM_DATA_KEY		"GUI/Menu/Item"

/*
 *	Menu Item Activate Data:
 *
 *	Used to provide the widget, callback function, and data
 *	information to pass to the callback function when a menu item's
 *	"activate" signal is called.
 */
typedef struct {

	GtkWidget	*w;			/* GtkMenuItem */

	void		(*cb)(
			GtkWidget *w,
			gpointer
	);
	gpointer	data;

} GUIMenuItemActivateData;
#define GUI_MENU_ITEM_ACTIVATE_DATA(p)	((GUIMenuItemActivateData *)(p))
#define GUI_MENU_ITEM_ACTIVATE_DATA_KEY	"GUI/Menu/Item/Activate"


/*
 *	GtkEditable Popup Menu:
 */
typedef struct {

	GtkWidget	*menu,
					*editable,
					*undo_mi,
					*undo_msep,
					*cut_mi,
					*copy_mi,
					*paste_mi,
					*delete_mi,
					*select_msep,
					*select_all_mi,
					*unselect_all_mi;

	gint		freeze_count;
	gui_editable_popup_menu_flags	flags;

	gchar		*undo_buf,
					*redo_buf;
	gint		undo_last_pos,
					redo_last_pos;

} GUIEditablePopupMenuData;
#define GUI_EDITABLE_POPUP_MENU_DATA(p)		((GUIEditablePopupMenuData *)(p))
#define GUI_EDITABLE_POPUP_MENU_DATA_KEY	"GUI/Editable/PopupMenu"


/*
 *	GtkTooltips Group:
 *
 *	Used as the tool tips group for all GUI*() functions.
 */
static GtkTooltips	*gui_tooltips = NULL;
static gboolean		gui_tooltips_state;	/* Global enabled or disabled */


static gchar *G_STRCAT(gchar *sa, const gchar *sb);


/* Get Key Name */
static gchar *GUIGetKeyName(guint key);

/* Block Input Callbacks */
static void GUIBlockInputDataDestroyCB(gpointer data);
static gint GUIBlockInputEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);

/* Dynamic Data Exchange Callbacks */
static void GUISelectionDataDelete(GUISelectionData *sel_data);
static void GUIDDEOwnerDestroyCB(gpointer data);
static gboolean GUIDDESelectionClearEventCB(
	GtkWidget *widget, GdkEventSelection *event, gpointer data
);
static void GUIDDESelectionGetCB(
	GtkWidget *widget, GtkSelectionData *selection_data,
	guint info, guint t, gpointer data
);
static void GUIDDESelectionReceivedCB(
	GtkWidget *widget, GtkSelectionData *selection_data, guint t,
	gpointer data
);

/* Drag & Drop Drag Icon Callbacks */
static void GUIDNDDragIconDestroyCB(gpointer data);

/* Drag & Drop Source Callbacks */
static void GUIDNDDragBeginCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
);
static void GUIDNDDragEndCB(
	GtkWidget *widget, GdkDragContext *dc,
	gpointer data
);
static void GUIDNDSourceDataDestroyCB(gpointer data);

/* Drag & Drop Target Callbacks */
static gint GUIDNDTargetHighlightDrawCB(gpointer data);
static gint GUIDNDTargetUnhighlightDrawCB(gpointer data);
static void GUIDNDTargetDragLeaveCB(
	GtkWidget *widget, GdkDragContext *dc,
	guint t,
	gpointer data
);
static gboolean GUIDNDTargetDragMotionCB(
	GtkWidget *widget, GdkDragContext *dc,
	gint x, gint y,
	guint t,
	gpointer data
);
static void GUIDNDTargetDataDestroyCB(gpointer data);

/* Button Callbacks */
static void GUIButtonDestroyCB(gpointer data);
static void GUIButtonStateChangedCB(
	GtkWidget *widget, GtkStateType previous_state, gpointer data
);

/* Banner Callbacks */
static void GUIBannerDestroyCB(gpointer data);
static void GUIBannerRealizeCB(GtkWidget *widget, gpointer data);
static gint GUIBannerEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);

/* GtkCombo Callbacks */
static void GUIComboActivateCB(GtkWidget *widget, gpointer data);
static void GUIComboChangedCB(GtkWidget *widget, gpointer data);
static void GUIComboDataDestroyCB(gpointer data);

/* GtkMenuItem Callbacks */
static void GUIMenuItemActivateCB(gpointer data);
static void GUIMenuItemActivateDataDestroyCB(gpointer data);
static void GUIMenuItemDataDestroyCB(gpointer data);

/* GtkEditable Popup Menu Callbacks */
static void GUIEditablePopupMenuDataDestroyCB(gpointer data);
static void GUIEditablePopupMenuUndoCB(GtkWidget *widget, gpointer data);
static void GUIEditablePopupMenuCutCB(GtkWidget *widget, gpointer data);
static void GUIEditablePopupMenuCopyCB(GtkWidget *widget, gpointer data);
static void GUIEditablePopupMenuPasteCB(GtkWidget *widget, gpointer data);
static void GUIEditablePopupMenuDeleteCB(GtkWidget *widget, gpointer data);
static void GUIEditablePopupMenuSelectAllCB(GtkWidget *widget, gpointer data);
static void GUIEditablePopupMenuUnselectAllCB(GtkWidget *widget, gpointer data);
static gint GUIEditablePopupMenuEditableEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);

/* Pull Out Callbacks */
static void GUIPullOutDataDestroyCB(gpointer data);
static void GUIPullOutDrawHandleDrawCB(
	GtkWidget *widget, GdkRectangle *area, gpointer data
);
static gint GUIPullOutDrawHandleCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
static gint GUIPullOutPullOutBtnCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
static gint GUIPullOutCloseCB(GtkWidget *widget, GdkEvent *event, gpointer data);


/* GtkRcStyle Utilities */
GtkRcStyle *GUIRCStyleCopy(const GtkRcStyle *rcstyle);
void GUIRCStyleSetRecursive(GtkWidget *w, GtkRcStyle *rcstyle);


/* Geometry */
GdkGeometryFlags GUIParseGeometry(
	const gchar *s,
	gint *x, gint *y, gint *width, gint *height
);
gboolean GUICListGetCellGeometry(
	GtkCList *clist,
	const gint column, const gint row,
	gint *x, gint *y, gint *width, gint *height
);

/* Display */
gpointer GUIGetDisplay(void);

/* Root/Desktop */
GdkWindow *GUIGetWindowRoot(void);
void GUIGetWindowRootPosition(
	GdkWindow *window,
	gint *x_rtn, gint *y_rtn
);
void GUIGetPositionRoot(
	GdkWindow *w,
	const gint x, const gint y,
	gint *x_rtn, gint *y_rtn
);

/* GtkCTreeNode Utilities */
GtkCTreeNode *GUICTreeNodeGetParent(GtkCTreeNode *node);
GtkCTreeNode *GUICTreeNodeGetChild(GtkCTreeNode *node);
GtkCTreeNode *GUICTreeNodeGetSibling(GtkCTreeNode *node);
gboolean GUICTreeNodeGetExpanded(GtkCTreeNode *node);
gint GUICTreeNodeRow(
	GtkCTree *ctree,
	GtkCTreeNode *node
);
static void GUICTreeNodeDeltaRowsIterate(
	GtkCTree *ctree,
	GtkCTreeNode *node,
	GtkCTreeNode *end,
	gint *row_count,
	gboolean *end_found
);
gint GUICTreeNodeDeltaRows(
	GtkCTree *ctree,
	GtkCTreeNode *start,	/* Use NULL for first/toplevel node */
	GtkCTreeNode *end
);

/* String Utilities */
void GUIGetTextBounds(
	GdkFont *font,
	const gchar *text, const gint text_length,
	GdkTextBounds *bounds
);
void GUIGetStringBounds(
	GdkFont *font,
	const gchar *string,
	GdkTextBounds *bounds
);


/* GdkGC */
GdkGC *GUIGCNew(GdkWindow *window);
GdkGC *GUIGCUnref(GdkGC *gc);


/* Block Input */
void GUIBlockInput(
	GtkWidget *w,
	const gboolean block
);
gint GUIBlockInputGetLevel(GtkWidget *w);


/* GdkEvents */
void GUIEventsProcessThrough(void);
void GUIEventsProcessCurrent(void);
void GUIEventsProcessUntil(const gulong limit_ms);


/* Timeout & Idle */
guint GUITimeoutRemove(const guint id);
guint GUIIdleRemove(const guint id);


/* GdkWindow Utilities */
gint GUIWindowGetRefCount(GdkWindow *window);


/* Window Manager (WM) Utilities */
void GUISetWMIcon(
	GdkWindow *window,
	guint8 **data
);
void GUISetWMIconFile(
	GdkWindow *window,
	const gchar *path
);


/* GtkWindow Utilities */
void GUIWindowApplyArgs(
	GtkWindow *w,
	const gint argc, gchar **argv
);
gboolean GUIIsWindowArg(const gchar *arg);

/* GtkCTree Utilities */
gboolean GUICTreeOptimizeExpandPosition(
	GtkCTree *ctree,
	GtkCTreeNode *node
);


/* GdkFont */
GdkFont *GUIFontUnref(GdkFont *font);


/* GdkColormap */
GdkColormap *GUIColormapUnref(GdkColormap *colormap);


/* GdkBitmap */
GdkBitmap *GUIBitmapNew(
	GdkWindow *window,
	const gint width, const gint height
);
GdkBitmap *GUICreateBitmapFromDataRGBA(
	const gint width, const gint height,
	const gint bpl,
	const guint8 *rgba,
	const guint8 threshold,
	GdkWindow *window
);
GdkBitmap *GUIBitmapUnref(GdkBitmap *bitmap);

/* GdkPixmap */
GdkPixmap *GUIPixmapNew(
	GdkWindow *window,
	const gint width, const gint height,
	const gint depth
);
GdkPixmap *GUIPixmapNewFromXPMData(
	GdkWindow *window,
	GdkBitmap **mask_rtn,
	GdkColor *bg_color,
	guint8 **data
);
GdkPixmap *GUIPixmapNewFromXPMFile(
	GdkWindow *window,
	GdkBitmap **mask_rtn,
	GdkColor *bg_color,
	const gchar *path
);
GtkWidget *GUIGtkPixmapNewFromXPMData(
	GdkWindow *window,
	GdkColor *bg_color,
	guint8 **data
);
GtkWidget *GUICreateMenuItemIcon(guint8 **icon_data);
GdkPixmap *GUIPixmapUnref(GdkPixmap *pixmap);


/* GtkObjects */
GtkObject *GUIObjectRef(GtkObject *o);
GtkObject *GUIObjectUnref(GtkObject *o);


/* GtkAdjustments */
void GUIAdjustmentSetValue(
	GtkAdjustment *adj,
	const gfloat v,
	const gboolean emit_value_changed
);


/* GtkWidget & GtkWindow Mapping */
void GUIWidgetMapRaise(GtkWidget *w);
void GUIWindowMinimize(GtkWindow *window);


/* Tooltips */
static void GUICreateGlobalTooltips(void);
void GUISetWidgetTip(
	GtkWidget *w,
	const gchar *tip
);
void GUIShowTipsNow(GtkWidget *w);
void GUISetGlobalTipsState(const gboolean state);


/* Buttons With Icon & Label */
void GUIButtonChangeLayout(
	GtkWidget *w,
	const gboolean show_pixmap,
	const gboolean show_label
);
void GUIButtonLabelUnderline(
	GtkWidget *w,
	const guint c
);
void GUIButtonPixmapUpdate(
	GtkWidget *w,
	guint8 **icon,
	const gchar *label
);
void GUIButtonArrowUpdate(
	GtkWidget *w,
	GtkArrowType arrow_type,
	const gint arrow_width, const gint arrow_height,
	const gchar *label
);
GtkWidget *GUIButtonGetMainBox(GtkWidget *w);
GtkWidget *GUIButtonGetLabel(GtkWidget *w);
GtkWidget *GUIButtonGetPixmap(GtkWidget *w);
GtkWidget *GUIButtonGetArrow(GtkWidget *w);
const gchar *GUIButtonGetLabelText(GtkWidget *w);
static GtkWidget *GUIButtonPixmapLabelHV(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn,
	gboolean horizontal
);
GtkWidget *GUIButtonPixmapLabelH(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn
);
GtkWidget *GUIButtonPixmapLabelV(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn
);
GtkWidget *GUIButtonPixmap(guint8 **icon);

/* Buttons With Arrow & Label */
static GtkWidget *GUIButtonArrowLabelHV(
	const GtkArrowType arrow_type,
	const gint arrow_width,
	const gint arrow_height,
	const gchar *label,
	GtkWidget **label_rtn,
	const gboolean horizontal
);
GtkWidget *GUIButtonArrowLabelH(
	GtkArrowType arrow_type, gint arrow_width, gint arrow_height, 
	const gchar *label, GtkWidget **label_rtn
);
GtkWidget *GUIButtonArrowLabelV(
	GtkArrowType arrow_type, gint arrow_width, gint arrow_height,
	const gchar *label, GtkWidget **label_rtn
);
GtkWidget *GUIButtonArrow(
	GtkArrowType arrow_type, gint arrow_width, gint arrow_height
);

/* Toggle Buttons With Icon & Label */
static GtkWidget *GUIToggleButtonPixmapLabelHV(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn,
	const gboolean horizontal
);
GtkWidget *GUIToggleButtonPixmapLabelH(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn
);
GtkWidget *GUIToggleButtonPixmapLabelV(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn
);
GtkWidget *GUIToggleButtonPixmap(guint8 **icon);


/* Prompts With Icon, Label, Entry, and Browse */
static GtkWidget *GUIPromptBarOrBrowse(
	guint8 **icon, const gchar *label,
	gpointer *label_rtn, gpointer *entry_rtn, gpointer *browse_rtn
);
GtkWidget *GUIPromptBarWithBrowse(
	guint8 **icon, const gchar *label,
	gpointer *label_rtn, gpointer *entry_rtn, gpointer *browse_rtn,
	gpointer browse_data, void (*browse_cb)(GtkWidget *, gpointer)
);
GtkWidget *GUIPromptBar(
	guint8 **icon, const gchar *label,
	gpointer *label_rtn, gpointer *entry_rtn
);


/* GtkTargetEntry */
GtkTargetEntry *gtk_target_entry_new(void);
GtkTargetEntry *gtk_target_entry_copy(const GtkTargetEntry *entry);
void gtk_target_entry_delete(GtkTargetEntry *entry);


/* Dynamic Data Exchange */
void GUIDDESetDirect(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	GdkAtom target_atom,
	guint8 *data, const gint length
);
void GUIDDESet(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	GdkAtom target_atom,
	const guint8 *data, const gint length
);
guint8 *GUIDDEGet(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	GdkAtom target_atom,
	gint *length
);
void GUIDDEClear(
	GtkWidget *w,
	GdkAtom selection_atom
);
void GUIDDESetBinary(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	const guint8 *data, const gint length
);
guint8 *GUIDDEGetBinary(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	gint *length
);
void GUIDDESetString(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	const gchar *data
);
gchar *GUIDDEGetString(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t
);


/* Drag & Drop */
void GUIDNDSetSrc(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction actions,
	const GdkModifierType buttons,
	void (*drag_begin_cb)(
			GtkWidget *, GdkDragContext *,
			gpointer
	),
	void (*drag_data_get_cb)(
			GtkWidget *, GdkDragContext *,
			GtkSelectionData *,
			guint, guint,
			gpointer
	),
	void (*drag_data_delete_cb)(
			GtkWidget *, GdkDragContext *,
			gpointer
	),
	void (*drag_end_cb)(
			GtkWidget *, GdkDragContext *,
			gpointer
	),
	gpointer data
);
void GUIDNDSetSrcTypes(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction actions,
	const GdkModifierType buttons
);
void GUIDNDSetTar(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction actions,
	const GdkDragAction default_action_same,
	const GdkDragAction default_action,
	void (*drag_data_recieved_cb)(
			GtkWidget *, GdkDragContext *,
			gint, gint,
			GtkSelectionData *,
			guint, guint,
			gpointer     
	),
	gpointer drag_data_received_data,
	const gboolean highlight
);
void GUIDNDSetTarTypes(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction actions,
	const GdkDragAction default_action_same,
	const GdkDragAction default_action,
	const gboolean highlight
);
void GUIDNDGetTarActions(
	GtkWidget *w,
	GdkDragAction *allowed_actions_rtn,
	GdkDragAction *default_action_same_rtn,
	GdkDragAction *default_action_rtn
); 
GdkDragAction GUIDNDDetermineDragAction(
	GdkDragAction requested_actions,
	GdkDragAction allowed_actions,
	GdkDragAction default_action
);
gboolean GUIDNDIsTargetAccepted(
	GtkWidget *w,
	const gchar *name,
	const GtkTargetFlags flags
);
static void GUIDNDSetDragIconFromWidgetNexus(
	GtkWidget *w,
	GList *cells_list,
	const gint nitems,
	const GtkOrientation layout_orientation,
	const GtkOrientation icon_text_orientation,
	const gint item_spacing,
	const gboolean drop_shadow,
	const gint hot_x, const gint hot_y
);
void GUIDNDSetDragIconFromCellsList(
	GtkWidget *w,
	GList *cells_list,
	const gint nitems,
	const GtkOrientation layout_orientation,
	const GtkOrientation icon_text_orientation,
	const gint item_spacing
);
void GUIDNDSetDragIconFromCListSelection(GtkCList *clist);
void GUIDNDSetDragIconFromCListCell(
	GtkCList *clist,
	const gint row, const gint column
);
void GUIDNDSetDragIconFromCList(GtkCList *clist);
void GUIDNDSetDragIconFromCTreeSelection(GtkCTree *ctree);
void GUIDNDSetDragIconFromCTreeNode(
	GtkCTree *ctree,
	GtkCTreeNode *node,
	const gint column
);
void GUIDNDSetDragIcon(
	GtkWidget *w,
	GdkPixmap *pixmap, GdkBitmap *mask,
	const gint hot_x, const gint hot_y
);
void GUIDNDClearDragIcon(GtkWidget *w);


/* Banners */
GtkWidget *GUIBannerCreate(
	const gchar *label,
	GdkFont *font,
	GdkColor *color_fg,
	GdkColor *color_bg,
	GdkColor *color_border,
	const GtkJustification justify,
	const gboolean expand,
	const gint inside_border,
	const gint outside_border
);
void GUIBannerDraw(GtkWidget *w);


/* Combo With Label, Combo, Initial Value, and Initial List */
GtkWidget *GUIComboCreate(
	const gchar *label,	/* Label */
	const gchar *text,	/* Entry Value */
	GList *list,		/* Combo List */
	gint max_items,		/* Maximum Items In Combo List */
	gpointer *combo_rtn,	/* GtkCombo Return */
	gpointer data,
	void (*func_cb)(GtkWidget *w, gpointer),
	void (*list_change_cb)(GtkWidget *, gpointer, GList *)
);
void GUIComboActivateValue(GtkWidget *w, const gchar *value);
void GUIComboAddItem(GtkWidget *w, const gchar *value);
GList *GUIComboGetList(GtkWidget *w);
void GUIComboSetList(GtkWidget *w, GList *list);
void GUIComboClearAll(GtkWidget *w);


/* GtkMenuBars & GtkMenuItems */
GtkWidget *GUIMenuBarCreate(GtkAccelGroup **accelgrp_rtn);
GtkWidget *GUIMenuCreateTearOff(void);
GtkWidget *GUIMenuCreate(void);
GtkWidget *GUIMenuItemCreate(
	GtkWidget *menu,
	const gui_menu_item_type type,
	GtkAccelGroup *accelgrp,
	guint8 **icon,
	const gchar *label,
	const guint accel_key, const guint accel_mods,
	void (*func_cb)(GtkWidget *w, gpointer),
	gpointer data
);
void GUISetMenuItemSetDefault(
	GtkWidget *w,
	const gboolean is_default
);
void GUISetMenuItemCrossingCB(
	GtkWidget *w,
	gint (*enter_cb)(GtkWidget *, GdkEvent *, gpointer),
	gpointer enter_data,
	gint (*leave_cb)(GtkWidget *, GdkEvent *, gpointer),
	gpointer leave_data
);
GtkWidget *GUIMenuAddToMenuBar(
	GtkWidget *menu_bar,
	GtkWidget *menu,
	const gchar *label,
	const GtkJustification justify
);
void GUIMenuItemSetLabel(
	GtkWidget *menu_item,
	const gchar *label
);
GtkWidget *GUIMenuItemGetLabelWidget(GtkWidget *menu_item);
void GUIMenuItemSetPixmap(
	GtkWidget *menu_item,
	guint8 **icon_data
);
void GUIMenuItemSetPixmap2(
	GtkWidget *menu_item,
	GdkPixmap *pixmap, GdkBitmap *mask
);
void GUIMenuItemSetAccelKey(
	GtkWidget *menu_item,
	GtkAccelGroup *accelgrp,
	const guint accel_key, const guint accel_mods
);
void GUIMenuItemSetCheck(
	GtkWidget *menu_item,
	const gboolean active,
	const gboolean emit_signal
);
gboolean GUIMenuItemGetCheck(GtkWidget *menu_item);
GtkWidget *GUIMenuAddToMenuBarPixmapH(
	GtkWidget *menu_bar,
	GtkWidget *menu,
	const gchar *label,
	guint8 **icon_data,
	const GtkJustification justify
);
void GUIMenuItemSetSubMenu(
	GtkWidget *menu_item,
	GtkWidget *sub_menu
);


/* Editable Popup Menu */
static void GUIEditablePopupMenuRecordUndo(GUIEditablePopupMenuData *d);
static void GUIEditablePopupMenuUpdate(GUIEditablePopupMenuData *d);
GtkWidget *GUIEditableGetPopupMenu(GtkWidget *w);
void GUIEditableEndowPopupMenu(
	GtkWidget *w, const gui_editable_popup_menu_flags flags
);


/* Pull Out Window */
void *GUIPullOutCreateH(
	void *parent_box,
	int homogeneous, int spacing,		/* Of client vbox */
	int expand, int fill, int padding,	/* Of holder hbox */
	int toplevel_width, int toplevel_height,
	void *pull_out_client_data,
	void (*pull_out_cb)(void *, void *),
	void *push_in_client_data,
	void (*push_in_cb)(void *, void *)
);
void *GUIPullOutGetToplevelWindow(
	void *client_box,
	int *x, int *y, int *width, int *height
);
void GUIPullOutPullOut(void *client_box);
void GUIPullOutPushIn(void *client_box); 


#ifndef ISBLANK
# define ISBLANK(c)	(((c) == ' ') || ((c) == '\t'))
#endif

#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) ? (gint)strlen(s) : 0)
#define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : TRUE)


static gchar *G_STRCAT(gchar *sa, const gchar *sb)
{
	if(sa != NULL) {
		if(sb != NULL) {
			gchar *sc = g_strconcat(sa, sb, NULL);
			if(sc != NULL)
			{
				g_free(sa);
				sa = sc;
			}
		}
	} else {
		if(sb != NULL)
			sa = g_strdup(sb);
		else
			sa = g_strdup("");
	}
	return(sa);
}


/*
 *	Returns a statically allocated string describing the specified
 *	key.
 */
static gchar *GUIGetKeyName(guint key)
{
	switch(key)
	{
	  case GDK_BackSpace:
		return("BackSpace");
	  case GDK_Tab:
		return("Tab");
	  case GDK_Linefeed:
		return("LineFeed");
	  case GDK_Clear:
		return("Clear");
	  case GDK_Return:
		return("Return");
	  case GDK_Pause:
		return("Pause");
	  case GDK_Scroll_Lock:
		return("ScrollLock");
	  case GDK_Sys_Req:
		return("SysReq");
	  case GDK_Escape:
		return("Escape");
	  case GDK_space:
		return("Space");
	  case GDK_Delete:
		return("Delete");
	  case GDK_Home:
		return("Home");
	  case GDK_Left:
		return("Left");
	  case GDK_Right:
		return("Right");
	  case GDK_Down:
		return("Down");
	  case GDK_Up:
		return("Up");
	  case GDK_Page_Up:
		return("PageUp");
	  case GDK_Page_Down:
		return("PageDown");
	  case GDK_End:
		return("End");
	  case GDK_Begin:
		return("Begin");
	  case GDK_Select:
		return("Select");
	  case GDK_Print:
		return("Print");
	  case GDK_Execute:
		return("Execute");
	  case GDK_Insert:
		return("Insert");
	  case GDK_Undo:
		return("Undo");
	  case GDK_Redo:
		return("Redo");
	  case GDK_Menu:
		return("Menu");
	  case GDK_Find:
		return("Find");
	  case GDK_Cancel:
		return("Cancel");
	  case GDK_Help:
		return("Help");
	  case GDK_Break:
		return("Break");
	  case GDK_Num_Lock:
		return("NumLock");

	  case GDK_KP_Space:
		return("KPSpace");
	  case GDK_KP_Tab:
		return("KBTab");
	  case GDK_KP_Enter:
		return("KPEnter");
	  case GDK_KP_F1:
		return("KPF1");
	  case GDK_KP_F2:
		return("KPF2");
	  case GDK_KP_F3:
		return("KPF3");
	  case GDK_KP_F4:
		return("KPF4");
	  case GDK_KP_Home:
		return("KPHome");
	  case GDK_KP_Left:
		return("KPLeft");
	  case GDK_KP_Up:
		return("KPUp");
	  case GDK_KP_Right:
		return("KPRight");
	  case GDK_KP_Down:
		return("KPDown");
	  case GDK_KP_Page_Up:
		return("KPPageUp");
	  case GDK_KP_Page_Down:
		return("KPPageDown");
	  case GDK_KP_End:
		return("KPEnd");
	  case GDK_KP_Begin:
		return("KPBegin");
	  case GDK_KP_Insert:
		return("KPInsert");
	  case GDK_KP_Delete:
		return("KPDelete");
	  case GDK_KP_Equal:
		return("KPEqual");
	  case GDK_KP_Multiply:
		return("KPMultiply");
	  case GDK_KP_Add:
		return("KPAdd");
	  case GDK_KP_Separator:
		return("KPSeparator");
	  case GDK_KP_Subtract:
		return("KPSubtract");
	  case GDK_KP_Decimal:
		return("KPDecimal");
	  case GDK_KP_Divide:
		return("KPDivide");
	  case GDK_KP_0:
		return("KP0");
	  case GDK_KP_1:
		return("KP1");
	  case GDK_KP_2:
		return("KP2");
	  case GDK_KP_3:
		return("KP3");
	  case GDK_KP_4:
		return("KP4");
	  case GDK_KP_5:
		return("KP5");
	  case GDK_KP_6:
		return("KP6");
	  case GDK_KP_7:
		return("KP7");
	  case GDK_KP_8:
		return("KP8");
	  case GDK_KP_9:
		return("KP9");

	  case GDK_F1:
		return("F1");
	  case GDK_F2:
		return("F2");
	  case GDK_F3:
		return("F3");
	  case GDK_F4:
		return("F4");
	  case GDK_F5:
		return("F5");
	  case GDK_F6:
		return("F6");
	  case GDK_F7:
		return("F7");
	  case GDK_F8:
		return("F8");
	  case GDK_F9:
		return("F9");
	  case GDK_F10:
		return("F10");
	  case GDK_F11:
		return("F11");
	  case GDK_F12:
		return("F12");
	  case GDK_F13:
		return("F13");
	  case GDK_F14:
		return("F14");
	  case GDK_F15:
		return("F15");
	  case GDK_F16:
		return("F16");
	  case GDK_F17:
		return("F17");
	  case GDK_F18:
		return("F18");
	  case GDK_F19:
		return("F19");
	  case GDK_F20:
		return("F20");
	  case GDK_F21:
		return("F21");
	  case GDK_F22:
		return("F22");
	  case GDK_F23:
		return("F23");
	  case GDK_F24:
		return("F24");
	  case GDK_F25:
		return("F25");
	  case GDK_F26:
		return("F26");
	  case GDK_F27:
		return("F27");
	  case GDK_F28:
		return("F28");
	  case GDK_F29:
		return("F29");
	  case GDK_F30:
		return("F30");

	  case GDK_Shift_L:
		return("ShiftL");
	  case GDK_Shift_R:
		return("ShiftR");
	  case GDK_Control_L:
		return("ControlL");
	  case GDK_Control_R:
		return("ControlR");
	  case GDK_Caps_Lock:
		return("CapsLock");
	  case GDK_Shift_Lock:
		return("ShiftLock");
	  case GDK_Meta_L:
		return("MetaL");
	  case GDK_Meta_R:
		return("MetaR");
	  case GDK_Alt_L: 
		return("AltL");
	  case GDK_Alt_R:
		return("AltR");
	  case GDK_Super_L:
		return("SuperL");
	  case GDK_Super_R: 
		return("SuperR");
	  case GDK_Hyper_L: 
		return("HyperL");
	  case GDK_Hyper_R: 
		return("HyperR");
	  default:
		return("");
	}
}


/*
 *	Block input data "destroy" signal callback.
 */
static void GUIBlockInputDataDestroyCB(gpointer data)
{
	GUIBlockInputData *d = GUI_BLOCK_INPUT_DATA(data);
	if(d == NULL)
		return;

	gtk_widget_destroy(d->invisible);
	g_free(d);
}

/*
 *	Block input any GdkEvent "stealer" callback.
 */
static gint GUIBlockInputEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	gint status = FALSE;
	GtkWidget *ew;
	GUIBlockInputData *d = GUI_BLOCK_INPUT_DATA(data);
	if(d == NULL)
		return(FALSE);

	/* Get the GtkWidget that this event occured over */
	ew = gtk_get_event_widget(event);
	if(ew != NULL)
	{
		/* Did this event not occure over the blocked GtkWidget? */
		GtkWidget *ew_toplevel = gtk_widget_get_toplevel(ew);
		if(ew_toplevel != d->w)
		{
			/* Pass this event on to its intended widget */
			status = gtk_widget_event(ew, event);
		}
	}

	return(status);
}


/*
 *	Deletes the GUISelectionData.
 */
static void GUISelectionDataDelete(GUISelectionData *sel_data)
{
	if(sel_data == NULL)
		return;

	g_free(sel_data->data);
	g_free(sel_data);
}

/*
 *	DDE Owner data "destroy" signal callback.
 */
static void GUIDDEOwnerDestroyCB(gpointer data)
{
	GUISelectionOwner *owner = GUI_SELECTION_OWNER(data);
	if(owner == NULL)
		return;

	/* Delete the selection data */
	if(owner->datas_list != NULL)
	{
		g_list_foreach(
			owner->datas_list,
			(GFunc)GUISelectionDataDelete,
			NULL
		);
		g_list_free(owner->datas_list);
	}

	/* Do not disconnect the signal functions, they are already
	 * disconnected when the GtkWidget was destroyed
	 */

	g_free(owner);
}

/*
 *	DDE GtkWidget "selection_clear_event" signal callback.
 */
static gboolean GUIDDESelectionClearEventCB(
	GtkWidget *widget, GdkEventSelection *event, gpointer data
)
{
	GUISelectionOwner *owner = GUI_SELECTION_OWNER(data);
	if((widget == NULL) || (owner == NULL))
		return(FALSE);

	/* Clear the selection data */
	if(event != NULL)
	{
		const GdkAtom selection_atom = event->selection;
		GList *glist;
		GUISelectionData *sel_data;
		for(glist = owner->datas_list;
			glist != NULL;
			glist = g_list_next(glist)
		)
		{
			sel_data = GUI_SELECTION_DATA(glist->data);
			if(sel_data == NULL)
				continue;

			/* Is this the selection data to clear? */
			if(sel_data->selection_atom == selection_atom)
			{
				sel_data->target_atom = GDK_NONE;
				sel_data->type_atom = GDK_NONE;
				g_free(sel_data->data);
				sel_data->data = NULL;
				sel_data->length = 0;
			}
		}
	}

	return(TRUE);
}

/*
 *	DDE GtkWidget "selection_get" signal callback.
 */
static void GUIDDESelectionGetCB(
	GtkWidget *widget, GtkSelectionData *selection_data,
	guint info, guint t, gpointer data
)
{
	gboolean data_sent;
	GList *glist;
	GUISelectionData *sel_data;
	GUISelectionOwner *owner = GUI_SELECTION_OWNER(data);
	if((widget == NULL) || (selection_data == NULL) ||
	   (owner == NULL)
	)
		return;

	/* Get the selection data by the specified selection */
	data_sent = FALSE;
	for(glist = owner->datas_list;
		glist != NULL;
		glist = g_list_next(glist)
	)
	{
		sel_data = GUI_SELECTION_DATA(glist->data);
		if(sel_data == NULL)
			continue;

		/* Is this the requested selection and target data? */
		if((selection_data->selection == sel_data->selection_atom) &&
		   (selection_data->target == sel_data->target_atom)
		)
		{
			gtk_selection_data_set(
				selection_data,
				sel_data->type_atom,
				8,				/* 8 bits */
				sel_data->data,
				sel_data->length
			);
			data_sent = TRUE;
			break;
		}
	}

	/* Error */
	if(!data_sent)
		gtk_selection_data_set(
			selection_data,
			GDK_NONE,			/* Type */
			0,				/* Bits */
			NULL,				/* Data */
			0				/* Length */
		);
}

/*
 *	DDE GtkWidget "selection_received" signal callback.
 */
static void GUIDDESelectionReceivedCB(
	GtkWidget *widget, GtkSelectionData *selection_data, guint t,
	gpointer data
)
{
	GdkAtom type;
	GUISelectionData *sel_rtn = GUI_SELECTION_DATA(data);
	if((widget == NULL) || (selection_data == NULL) ||
	   (sel_rtn == NULL)
	)
		return;

	/* Already got the data? */
	if(sel_rtn->data != NULL)
		return;

	/* Mark that the selection was received */
	sel_rtn->length = 0;

	/* No or empty data? */
	if((selection_data->data == NULL) ||
	   (selection_data->length <= 0)
	)
		return;

	/* Selection or type mismatch? */
	if((selection_data->selection != sel_rtn->selection_atom) ||
	   (selection_data->target != sel_rtn->target_atom)
	)
		return;

	/* Begin transfering the selection data to the return data
	 * by its type
	 */
	sel_rtn->type_atom = type = selection_data->type;

	/* Atoms List */
	if(type == GDK_SELECTION_TYPE_ATOM)
	{
		gint	i,
					m = selection_data->length / sizeof(GdkAtom);
		gchar	*s = NULL,
					*name;
		GdkAtom *atoms = (GdkAtom *)selection_data->data;

		for(i = 0; i < m; i++)
		{
			name = gdk_atom_name(atoms[i]);
			if(name != NULL)
			{
				s = G_STRCAT(s, name);
				if(i < (m - 1))
					s = G_STRCAT(s, " ");
				g_free(name);
			}
		}
		sel_rtn->data = s;
		sel_rtn->length = STRLEN(s);

		/* Change the return type to string */
		sel_rtn->type_atom = GDK_SELECTION_TYPE_STRING;
	}
	/* GdkBitmap */
	else if(type == GDK_SELECTION_TYPE_BITMAP)
	{
/* TODO */
	}
	/* GdkColormap */
	else if(type == GDK_SELECTION_TYPE_COLORMAP)
	{
/* TODO */
	}
	/* GdkDrawable */
	else if(type == GDK_SELECTION_TYPE_DRAWABLE)
	{
/* TODO */
	}
	/* Integer */
	else if(type == GDK_SELECTION_TYPE_INTEGER)
	{
		gint len = sel_rtn->length = selection_data->length;
		sel_rtn->data = g_malloc(len);
		memcpy(sel_rtn->data, selection_data->data, len);
	}
	/* GdkPixmap */
	else if(type == GDK_SELECTION_TYPE_PIXMAP)
	{
/* TODO */
	}
	/* GdkWindow */
	else if(type == GDK_SELECTION_TYPE_WINDOW)
	{
/* TODO */
	}
	/* String (latin-1 characters) */
	else if(type == GDK_SELECTION_TYPE_STRING)
	{
		/* Need to copy the data and append a null byte at the
		 * end (even if it already has one)
		 */
		const gint len = selection_data->length;
		gchar *s = (gchar *)g_malloc((len + 1) * sizeof(gchar));
		if(s != NULL)
		{
			memcpy(
				s,
				selection_data->data,
				len * sizeof(gchar)
			);
			s[len] = '\0';
			sel_rtn->data = s;
			sel_rtn->length = len;
		}
	}
	/* All else assume binary data */
	else
	{
		sel_rtn->data = g_memdup(
			selection_data->data,
			selection_data->length
		);
		sel_rtn->length = selection_data->length;
	}
}


/*
 *	DND Drag Icon "destroy" signal callback.
 */
static void GUIDNDDragIconDestroyCB(gpointer data)
{
	GUIDNDDragIcon *dnd_drag_icon = GUI_DND_DRAG_ICON(data);
	if(dnd_drag_icon == NULL)
		return;

	if(dnd_drag_icon->ref_count > 0)
		g_printerr(
"GUIDNDDragIconDestroyCB(): Warning: Destroying dnd_drag_icon=%p\
 with %i reference counts remaining.\n",
			dnd_drag_icon,
			dnd_drag_icon->ref_count
		);

	GTK_WIDGET_DESTROY(dnd_drag_icon->toplevel);

	g_free(dnd_drag_icon);
}


/*
 *	DND source "drag_begin" signal callback.
 */
static void GUIDNDDragBeginCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{
	GUIDNDDragIcon *dnd_drag_icon;
	GUIDNDSourceData *d = GUI_DND_SOURCE_DATA(data);
	if((widget == NULL) || (dc == NULL) || (d == NULL))
		return;

	dnd_drag_icon = GUI_DND_DRAG_ICON(gtk_object_get_data(
		GTK_OBJECT(widget),
		GUI_DND_DRAG_ICON_KEY
	));
	if(dnd_drag_icon != NULL)
	{
		dnd_drag_icon->ref_count++;

		/* Set the DND Icon if it is available */
		if(dnd_drag_icon->toplevel != NULL)
			gtk_drag_set_icon_widget(
				dc,
				dnd_drag_icon->toplevel,
				dnd_drag_icon->x,
				dnd_drag_icon->y
			);
	}
}

/*
 *	DND source "drag_end" signal callback.
 */
static void GUIDNDDragEndCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{
	GUIDNDDragIcon *dnd_drag_icon;
	GUIDNDSourceData *d = GUI_DND_SOURCE_DATA(data);
	if((widget == NULL) || (dc == NULL) || (d == NULL))
		return;

	dnd_drag_icon = GUI_DND_DRAG_ICON(gtk_object_get_data(
		GTK_OBJECT(widget),
		GUI_DND_DRAG_ICON_KEY
	));
	if(dnd_drag_icon != NULL)
	{
		dnd_drag_icon->ref_count--;
	}
}

/*
 *	DND source "destroy" signal callback.
 */
static void GUIDNDSourceDataDestroyCB(gpointer data)
{
	GUIDNDSourceData *d = GUI_DND_SOURCE_DATA(data);
	if(d == NULL)
		return;

	/* Remove this GtkWidget from the list of DND source GtkWidgets */
	if(d->widget != NULL)
		gui_dnd_source_widgets_list = g_list_remove(
			gui_dnd_source_widgets_list,
			d->widget
		);

	if(d->targets_list != NULL)
	{
		g_list_foreach(
			d->targets_list,
			(GFunc)gtk_target_entry_delete,
			NULL
		);
		g_list_free(d->targets_list);
	}

	g_free(d);
}


/*
 *	DND target highlight draw idle callback.
 *
 *	Clears the highlight_draw_sigid and draws the drag highlight
 *	on the GtkWidget plus any additional highlighting depending
 *	on the GtkWidget's type.
 *
 *	If the GUI_DND_TARGET_HIGHLIGHTED is not set then only the
 *	highlight_draw_sigid will be cleared.
 */
static gint GUIDNDTargetHighlightDrawCB(gpointer data)
{
	gint width, height;
	GdkDrawable *drawable;
	GtkStateType state;
	GtkStyle *style;
	GtkWidget *w;
	GUIDNDTargetData *d = GUI_DND_TARGET_DATA(data);
	if(d == NULL)
		return(FALSE);

	/* Clear this callback's signal id since we are handling it now */
	d->highlight_draw_sigid = 0;

	/* Do not draw the highlight if the highlighted flag is not set */
	if(!(d->flags & GUI_DND_TARGET_HIGHLIGHTED))
		return(FALSE);

	w = d->widget;
	if(w == NULL)
		return(FALSE);

	drawable = (GdkDrawable *)w->window;
	state = GTK_WIDGET_STATE(w);
	style = gtk_widget_get_style(w);
	if((drawable == NULL) || (style == NULL))
		return(FALSE);

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

	/* Highlight by the target GtkWidget's type since it may
	 * not automatically highlight itself because it may not be
	 * in focus
	 *
	 * GtkCList
	 */
	if(GTK_IS_CLIST(w))
	{
		gint row, column, list_start_y;
		GtkCList *clist = GTK_CLIST(w);
		const gboolean show_titles = GTK_CLIST_SHOW_TITLES(clist);
		GdkRectangle *column_title_area = &clist->column_title_area;

		/* Calculate the starting position of the list since we
		 * need to subtract this from the drag coordinates
		 * because the drag coordinates include the GtkCList's
		 * title area
		 */
		list_start_y = (show_titles) ?
			(column_title_area->y + column_title_area->height) : 0;

		/* Calculate the column and row that the drag is over */
		if(!gtk_clist_get_selection_info(
			clist,
			d->x,
			d->y - list_start_y,
			&row, &column
		))
		{
			row = -1;
			column = -1;
		}

		/* Change in drag position? */
		if(row != clist->focus_row)
		{
			GdkDrawable *drawable = (GdkDrawable *)clist->clist_window;
			GtkAdjustment	*vadj = clist->vadjustment,
							*hadj = clist->hadjustment;

			/* Set and redraw the focus row by having the widget
			 * draw itself normally without the focus row and
			 * then we will draw the focus row
			 */
			clist->focus_row = -1;
			gtk_widget_draw(w, NULL);
			clist->focus_row = row;

			/* Draw the focus row if it is visible */
			if((vadj != NULL) && (hadj != NULL) && (row > -1))
			{
				gint	x, y,
							width, height,

				/* Get the row height */
				row_height = GTK_CLIST_ROW_HEIGHT_SET(clist) ?
					clist->row_height : 0;
				if((row_height <= 0) && (style->font != NULL))
				{
					GdkFont *font = style->font;
					row_height = font->ascent + font->descent;
				}

				/* Account for spacing between rows */
				row_height += 1;

				/* Calculate the focus row's coordinates and size */
				x = -(gint)hadj->value;
				y = (row_height * row) - (gint)vadj->value;
				width = (gint)(hadj->upper - hadj->lower);
				height = row_height;

				/* Draw the focus row */
				gtk_paint_focus(
					style,
					(GdkWindow *)drawable,
					NULL,			/* Entire area */
					w,
					NULL,			/* No detail */
					x, y,
					width - 1, height - 1
				);
			}
		}
	}

	/* Draw the drag highlight around this GtkWidget */
	gtk_paint_focus(
		style,
		(GdkWindow *)drawable,
		NULL,				/* Entire area */
		w,
		NULL,				/* No detail */
		0, 0,
		width - 1, height - 1
	);

	return(FALSE);
}

/*
 *	DND target unhighlight draw idle callback.
 *
 *	Clears the unhighlight_draw_sigid and draws the GtkWidget
 *	normally without any highlight.
 *
 *	If the GUI_DND_TARGET_HIGHLIGHTED is still set then only the
 *	unhighlight_draw_sigid will be cleared.
 */
static gint GUIDNDTargetUnhighlightDrawCB(gpointer data)
{
	GtkWidget *w;
	GUIDNDTargetData *d = GUI_DND_TARGET_DATA(data);
	if(d == NULL)
		return(FALSE);

	/* Clear this callback's signal id since we are handling it now */
	d->unhighlight_draw_sigid = 0;

	/* Do not draw the GtkWidget normally if the highlighted flag
	 * is still set
	 */
	if(d->flags & GUI_DND_TARGET_HIGHLIGHTED)
		return(FALSE);

	w = d->widget;
	if(w == NULL)
		return(FALSE);

	/* Clear the focus based on the GtkWidget's type
	 *
	 * GtkCList
	 */
	if(GTK_IS_CLIST(w))
	{
		GtkCList *clist = GTK_CLIST(w);
		clist->focus_row = -1;
	}

	/* Unhighlight this GtkWidget by clearing the highlighed flag
	 * and queueing the GtkWidget to redraw normally
	 */
	d->flags &= ~GUI_DND_TARGET_HIGHLIGHTED;
	gtk_widget_draw(w, NULL);

	return(FALSE);
}

/*
 *	DND target "drag_leave" signal callback.
 */
static void GUIDNDTargetDragLeaveCB(
	GtkWidget *widget, GdkDragContext *dc,
	guint t,
	gpointer data
) 
{
	GUIDNDTargetData *d = GUI_DND_TARGET_DATA(data);
	if((widget == NULL) || (dc == NULL) || (d == NULL))
		return;

	/* Clear the drag over flag */
	if(d->flags & GUI_DND_TARGET_DRAG_OVER)
		d->flags &= ~GUI_DND_TARGET_DRAG_OVER;

	/* Clear the highlighted flag and queue the GtkWidget to
	 * redraw normally without the highlight
	 */
	if(d->flags & GUI_DND_TARGET_HIGHLIGHTED)
	{
		d->flags &= ~GUI_DND_TARGET_HIGHLIGHTED;

		if(d->unhighlight_draw_sigid == 0)
			d->unhighlight_draw_sigid = gtk_idle_add_priority(
				GTK_PRIORITY_REDRAW,
				GUIDNDTargetUnhighlightDrawCB, d
			);
	}
}

/*
 *	DND target "drag_motion" signal callback.
 *
 *	Responds with the appropriate drag action by calling
 *	gdk_drag_status() and performs highlighting.
 */
static gboolean GUIDNDTargetDragMotionCB(
	GtkWidget *widget, GdkDragContext *dc,
	gint x, gint y,
	guint t,
	gpointer data
)
{
	gboolean same_widget, same_process;
	gchar *target_name;
	GList *glist;
	GdkDragAction	requested_actions,
					allowed_actions,
					default_action,
					action;
	GtkTargetFlags target_flags;
	GtkWidget	*src_widget,
					*tar_widget;
	GUIDNDTargetData *d = GUI_DND_TARGET_DATA(data);
	if((widget == NULL) || (dc == NULL) || (d == NULL))
		return(FALSE);

	/* Set the drag over flag */
	if(!(d->flags & GUI_DND_TARGET_DRAG_OVER))
		d->flags |= GUI_DND_TARGET_DRAG_OVER;

	/* Record the drag position over this GtkWidget */
	d->x = x;
	d->y = y;

	/* Is the target GtkWidget not sensitive? */
	if(!GTK_WIDGET_SENSITIVE(widget))
	{
		/* Report that we will not accept this drag because the
		 * target GtkWidget is not sensitive
		 */
		gdk_drag_status(dc, 0, t);
		return(TRUE);
	}

	/* Get the GtkWidget that the drag originated from */
	src_widget = gtk_drag_get_source_widget(dc);

	/* Get the GtkWidget that the drag is currently over */
	tar_widget = widget;

	/* Are the two GtkWidgets the same? */
	same_widget = (src_widget == tar_widget) ? TRUE : FALSE;

	/* Is the source GtkWidget from the same process as the
	 * target GtkWidget?
	 */
	if(same_widget)
	{
		same_process = TRUE;
	}
	else
	{
		if(g_list_find(gui_dnd_source_widgets_list, src_widget) != NULL)
			same_process = TRUE;
		else
			same_process = FALSE;
	}

	/* Set the target flags to denote if this is the same
	 * GtkWidget or not
	 */
	target_flags = ((same_process) ? GTK_TARGET_SAME_APP : 0) |
		((same_widget) ? GTK_TARGET_SAME_WIDGET : 0);

	/* Iterate through the list of targets on the drag context
	 * and check if we will accept one of them
	 */
	for(glist = dc->targets;
		glist != NULL;
		glist = g_list_next(glist)
	)
	{
		/* Get the next target name on the drag context */
		target_name = gdk_atom_name((GdkAtom)glist->data);
		if(target_name != NULL)
		{
			/* Check if this target name is in the list of
			 * accepted targets on the GtkWidget
			 */
			if(GUIDNDIsTargetAccepted(
				tar_widget,
				target_name,
				target_flags
			))
			{
				g_free(target_name);
				break;
			}

			g_free(target_name);
		}
	}
	/* Drag not accepted? */
	if(glist == NULL)
	{
		/* Report that we will not accept this drag because none
		 * of its targets are in the list of accepted targets on
		 * the GtkWidget
		 */
		gdk_drag_status(dc, 0, t);
		return(TRUE);
	}

	/* Get the list of requested actions from the drag context */
	requested_actions = dc->actions;

	/* Get the list of allowed actions on the target GtkWidget */
	allowed_actions = d->allowed_actions;

	/* Get the default action on the target GtkWidget */
	default_action = same_widget ?
		d->default_action_same : d->default_action;

	/* Determine the appropriate drag action */
	action = GUIDNDDetermineDragAction(
		requested_actions,
		allowed_actions,
		default_action
	);

	/* Respond with the appropriate drag action */
	gdk_drag_status(dc, action, t);


	/* Highlight this GtkWidget? */
	if((d->flags & GUI_DND_TARGET_HIGHLIGHT) &&
	   GTK_WIDGET_SENSITIVE(tar_widget)
	)
	{
		/* Was a drag action determined? */
		if(action != 0)
		{
			/* Set the highlighted flag */
			if(!(d->flags & GUI_DND_TARGET_HIGHLIGHTED))
				d->flags |= GUI_DND_TARGET_HIGHLIGHTED;

			/* Queue the highlight draw regardless of if the
			 * GUI_DND_TARGET_HIGHLIGHTED flag was set or not since
			 * a drag motion may have changed to a different row
			 * on a list and the new row needs to be highlighted
			 */
			if(d->highlight_draw_sigid == 0)
				d->highlight_draw_sigid = gtk_idle_add_priority(
					GTK_PRIORITY_REDRAW,
					GUIDNDTargetHighlightDrawCB, d
				);
		}
		else
		{
			/* No drag actions were allowed, clear the highlighted
			 * flag and queue the GtkWidget to redraw normally
			 * without the highlight
			 */
			if(d->flags & GUI_DND_TARGET_HIGHLIGHTED)
			{
				d->flags &= ~GUI_DND_TARGET_HIGHLIGHTED;

				if(d->unhighlight_draw_sigid == 0)
					d->unhighlight_draw_sigid = gtk_idle_add_priority(
						GTK_PRIORITY_REDRAW,
						GUIDNDTargetUnhighlightDrawCB, d
					);
			}
		}
	}

	/* Return TRUE since we responded with gdk_drag_status() */
	return(TRUE);
}

/*
 *	DND target data "destroy" signal callback.
 */
static void GUIDNDTargetDataDestroyCB(gpointer data)
{
	GUIDNDTargetData *d = GUI_DND_TARGET_DATA(data);
	if(d == NULL)
		return;

	d->highlight_draw_sigid = GTK_IDLE_REMOVE(d->highlight_draw_sigid);
	d->unhighlight_draw_sigid = GTK_IDLE_REMOVE(d->unhighlight_draw_sigid);

	if(d->targets_list != NULL)
	{
		g_list_foreach(
			d->targets_list,
			(GFunc)gtk_target_entry_delete,
			NULL
		);
		g_list_free(d->targets_list);
	}

	g_free(d);
}


/*
 *	GtkButton "destroy" signal callback.
 */
static void GUIButtonDestroyCB(gpointer data)
{
	GUIButtonData *d = GUI_BUTTON_DATA(data);
	if(d == NULL)
		return;

	g_free(d->label_text);
	g_free(d);
}

/*
 *	GtkButton "state_changed" signal callback.
 */
static void GUIButtonStateChangedCB(
	GtkWidget *widget, GtkStateType previous_state, gpointer data
)
{
	GUIButtonData *d = GUI_BUTTON_DATA(data);
	if((widget == NULL) || (d == NULL))
		return;

	/* Is there a GtkArrow in this button? */
	if(d->arrow != NULL)
	{
		/* The GtkArrow's state needs to be reset when changing
		 * from GTK_STATE_INSENSITIVE to any other state because
		 * it apparently does not do so on its own
		 */
		if(previous_state == GTK_STATE_INSENSITIVE)
			gtk_widget_set_state(
				d->arrow,
				GTK_WIDGET_STATE(widget)
			);
	}
}


/*
 *	Banner "destroy" signal callback.
 */
static void GUIBannerDestroyCB(gpointer data)
{
	GUIBannerData *banner = GUI_BANNER_DATA(data);
	if(banner == NULL)
		return;

	GDK_GC_UNREF(banner->gc);

	if(banner->colormap != NULL)
	{
		GdkColormap *colormap = banner->colormap;

		GDK_COLORMAP_FREE_COLOR(colormap, banner->color_fg);
		g_free(banner->color_fg);
		GDK_COLORMAP_FREE_COLOR(colormap, banner->color_bg);
		g_free(banner->color_bg);
		GDK_COLORMAP_FREE_COLOR(colormap, banner->color_border);
		g_free(banner->color_border);

		(void)GDK_COLORMAP_UNREF(colormap);
	}

	(void)GDK_FONT_UNREF(banner->font);

	g_free(banner->label);

	g_free(banner);
}

/*
 *	Banner "realize" signal callback.
 */
static void GUIBannerRealizeCB(GtkWidget *widget, gpointer data)
{
	GdkWindow *window;
	GUIBannerData *banner = GUI_BANNER_DATA(data);
	if((widget == NULL) || (banner == NULL))
		return;

	window = widget->window;

	if(banner->colormap == NULL)
		banner->colormap = GDK_COLORMAP_REF(gtk_widget_get_colormap(widget));

	if(banner->colormap != NULL)
	{
		GdkColormap *colormap = banner->colormap;
		if(banner->color_fg != NULL)
			GDK_COLORMAP_ALLOC_COLOR(colormap, banner->color_fg);
		if(banner->color_bg != NULL)
			GDK_COLORMAP_ALLOC_COLOR(colormap, banner->color_bg);
		if(banner->color_border != NULL)
			GDK_COLORMAP_ALLOC_COLOR(colormap, banner->color_border);
	}

	if(banner->gc == NULL)
		banner->gc = GUIGCNew(window);
}

/*
 *	Banner event signal callback.
 */
static gint GUIBannerEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	gint status = FALSE;
	GUIBannerData *banner = GUI_BANNER_DATA(data);
	if((widget == NULL) || (event == NULL) || (banner == NULL))
		return(status);

	switch((gint)event->type)
	{
	  case GDK_CONFIGURE:
		status = TRUE;
		break;

	  case GDK_EXPOSE:
		GUIBannerDraw(widget);
		status = TRUE;
		break;
	}

	return(status);
}


/*
 *	GtkCombo "activate" signal callback.
 *
 *	First it will update the glist for the combo box and update
 *	the the combo's list. Then call (if not NULL) the specified
 *	activate callback function.
 */
static void GUIComboActivateCB(GtkWidget *widget, gpointer data)
{
	gint max_items;
	GtkWidget *parent, *wlabel;
	GtkCombo *combo;
	GList *glist_in;
	gpointer client_data;
	void (*func_cb)(GtkWidget *, gpointer);
	void (*list_change_cb)(GtkWidget *, gpointer, GList *);
	gchar *new_value;
	gpointer *cb_data = (gpointer *)data;
	if((widget == NULL) || (cb_data == NULL))
		return;

	/* Parse callback data, format is:
	 *
	 * table		Parent table that holds combo and label
	 * label		Label (may be NULL)
	 * combo		This combo widget
	 * GList		Contains list of strings in combo list
	 * max_items		Max items allowed in combo list
	 * client_data		Calling function set callback data
	 * func_cb		Calling function set callback function
	 * list_change_cb	Calling function set list change function
	 * NULL
	 */
	parent = (GtkWidget *)(cb_data[0]);
	wlabel = (GtkWidget *)(cb_data[1]);
	combo = (GtkCombo *)(cb_data[2]);
	glist_in = (GList *)(cb_data[3]);
	max_items = (gint)(cb_data[4]);
	client_data = (gpointer)(cb_data[5]);
	func_cb = cb_data[6];
	list_change_cb = cb_data[7];

	if(combo == NULL)
		return;

	/* Get new value string from combo's entry widget */
	new_value = gtk_entry_get_text(GTK_ENTRY(combo->entry));

	/* Make a duplicate of the recieved value or NULL if it is not
	 * available
	 */
	new_value = STRDUP(new_value);
	if(new_value == NULL)
		return;

	/* Add the new value to the combo box list, this will only
	 * be added if value is not already in the list. Excess items
	 * may be truncated if adding this item would cause items to
	 * exceed the set max items on the combo box
	 *
	 * List change callback will be called if the value was input
	 * to the combo box's list
	 */
	GUIComboAddItem((gpointer)combo, new_value);

	/* Call activate function */
	if(func_cb != NULL)
		func_cb(
			(GtkWidget *)combo,	/* Combo */
			client_data		/* Data */
		);

	/* Free new_value which we duplicated */
	g_free(new_value);
}

/*
 *	GtkCombo "changed" signal callback.
 */
static void GUIComboChangedCB(GtkWidget *widget, gpointer data)
{
	GList *glist;
	GtkCombo *combo;
	gpointer client_data;
	void (*list_change_cb)(GtkWidget *, gpointer, GList *);
	gpointer *cb_data = (gpointer *)data;
	if((widget == NULL) || (cb_data == NULL))
		return;

	/* Parse callback data, format is:
	 *
	 * table		Parent table that holds combo and label
	 * label		Label (may be NULL)
	 * combo		This combo widget
	 * GList		Contains list of strings in combo list
	 * max_items		Max items allowed in combo list
	 * client_data		Calling function set callback data
	 * func_cb		Calling function set callback function
	 * list_change_cb	Calling function set list change function
	 * NULL
	 */
	combo = (GtkCombo *)(cb_data[2]);
	glist = (GList *)cb_data[3];
	client_data = cb_data[5];
	list_change_cb = cb_data[7];

	/* Call list change function */
	if(list_change_cb != NULL)
		list_change_cb(
			(GtkWidget *)combo,	/* Combo */
			client_data,		/* Data */
			glist			/* List */
		);
}

/*
 *	Combo data destroy callback.
 */
static void GUIComboDataDestroyCB(gpointer data)
{
	GtkWidget *parent, *wlabel;
	GtkCombo *combo;
	GList *glist;
	gint max_items;
	gpointer client_data;
	void (*func_cb)(GtkWidget *, gpointer);
	void (*list_change_cb)(GtkWidget *, gpointer, GList *);
	gpointer *cb_data = (gpointer *)data;


	if(cb_data == NULL)
		return;

	/* Parse callback data, format is:
	 *
	 * table		Parent table that holds combo and label
	 * label		Label (may be NULL)
	 * combo		This combo widget
	 * GList		Contains list of strings in combo list
	 * max_items		Max items allowed in combo list
	 * client_data		Calling function set callback data
	 * func_cb		Calling function set callback function
	 * list_change_cb	Calling function set list change function
	 * NULL
	 */
	parent = (GtkWidget *)(cb_data[0]);
	wlabel = (GtkWidget *)(cb_data[1]);
	combo = (GtkCombo *)(cb_data[2]);
	glist = (GList *)(cb_data[3]);
	max_items = (gint)(cb_data[4]);
	client_data = (gpointer)(cb_data[5]);
	func_cb = cb_data[6];
	list_change_cb = cb_data[7];


	/* Do not call list change callback function on destroy */

	/* Begin deallocating data referenced on callback data */

	/* Delete list */
	if(glist != NULL)
	{
		g_list_foreach(glist, (GFunc)g_free, NULL);
		g_list_free(glist);
		glist = NULL;
	}

	/* Delete callback data */
	g_free(data);
}


/*
 *	Local menu item activate callback.
 */
static void GUIMenuItemActivateCB(gpointer data)
{
	GUIMenuItemActivateData *d = GUI_MENU_ITEM_ACTIVATE_DATA(data);
	if(d == NULL)
		return;

	if(d->cb != NULL)
		d->cb(
			d->w,				/* GtkMenuItem */
			d->data
		);
}

/*
 *	GUIMenuItemActivateData "destroy" callback.
 */
static void GUIMenuItemActivateDataDestroyCB(gpointer data)
{
	GUIMenuItemActivateData *d = GUI_MENU_ITEM_ACTIVATE_DATA(data);
	if(d == NULL)
		return;

	g_free(d);
}

/*
 *	GUIMenuItemData "destroy" callback.
 */
static void GUIMenuItemDataDestroyCB(gpointer data)
{
	GUIMenuItemData *d = GUI_MENU_ITEM_DATA(data);
	if(d == NULL)
		return;

	g_free(d->label_text);
	g_free(d->accel_text);
	g_free(d);
}


/*
 *	Editable popup menu data destroy callback.
 */
static void GUIEditablePopupMenuDataDestroyCB(gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	d->freeze_count++;

	GTK_WIDGET_DESTROY(d->menu);
	g_free(d->undo_buf);
	g_free(d->redo_buf);

	d->freeze_count--;

	g_free(d);
}

/*
 *	Editable popup menu undo/redo callback.
 */
static void GUIEditablePopupMenuUndoCB(GtkWidget *widget, gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	if(d->freeze_count > 0)
		return;

	if(!(d->flags & GUI_EDITABLE_POPUP_MENU_UNDO) ||
	   (d->flags & GUI_EDITABLE_POPUP_MENU_READ_ONLY)
	)
		return;

	d->freeze_count++;

	if(d->editable != NULL)
	{
		GtkEditable *editable = GTK_EDITABLE(d->editable);

		/* Redo? */
		if(d->redo_buf != NULL)
		{
			gint position = 0;

			g_free(d->undo_buf);
			d->undo_buf = gtk_editable_get_chars(editable, 0, -1);
			d->undo_last_pos = gtk_editable_get_position(editable);

			gtk_editable_delete_text(editable, 0, -1);
			gtk_editable_insert_text(editable, d->redo_buf, STRLEN(d->redo_buf), &position);
			gtk_editable_set_position(editable, d->redo_last_pos);

			g_free(d->redo_buf);
			d->redo_buf = NULL;
			d->redo_last_pos = 0;
		}
		/* Undo? */
		else if(d->undo_buf != NULL)
		{
			gint position = 0;

			g_free(d->redo_buf);
			d->redo_buf = gtk_editable_get_chars(editable, 0, -1);
			d->redo_last_pos = gtk_editable_get_position(editable);

			gtk_editable_delete_text(editable, 0, -1);
			gtk_editable_insert_text(editable, d->undo_buf, STRLEN(d->undo_buf), &position);
			gtk_editable_set_position(editable, d->undo_last_pos);

			g_free(d->undo_buf);
			d->undo_buf = NULL;
			d->undo_last_pos = 0;
		}
		else
		{
			g_free(d->redo_buf);
			d->redo_buf = gtk_editable_get_chars(editable, 0, -1);
			d->redo_last_pos = gtk_editable_get_position(editable);

			gtk_editable_delete_text(editable, 0, -1);
		}

		GUIEditablePopupMenuUpdate(d);
	}

	d->freeze_count--;
}

/*
 *	Editable popup menu cut callback.
 */
static void GUIEditablePopupMenuCutCB(GtkWidget *widget, gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	if(d->freeze_count > 0)
		return;

	if(d->flags & GUI_EDITABLE_POPUP_MENU_READ_ONLY)
		return;

	d->freeze_count++;

	GUIEditablePopupMenuRecordUndo(d);
	gtk_editable_cut_clipboard(GTK_EDITABLE(d->editable));
	GUIEditablePopupMenuUpdate(d);

	d->freeze_count--;
}

/*
 *	Editable popup menu copy callback.
 */                            
static void GUIEditablePopupMenuCopyCB(GtkWidget *widget, gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	if(d->freeze_count > 0)
		return;

	d->freeze_count++;

	gtk_editable_copy_clipboard(GTK_EDITABLE(d->editable));
	GUIEditablePopupMenuUpdate(d);

	d->freeze_count--;
}

/*
 *	Editable popup menu paste callback.
 */
static void GUIEditablePopupMenuPasteCB(GtkWidget *widget, gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	if(d->freeze_count > 0)
		return;

	if(d->flags & GUI_EDITABLE_POPUP_MENU_READ_ONLY)
		return;

	d->freeze_count++;

	GUIEditablePopupMenuRecordUndo(d);
	gtk_editable_paste_clipboard(GTK_EDITABLE(d->editable));
	GUIEditablePopupMenuUpdate(d);

	d->freeze_count--;
}

/*
 *	Editable popup menu delete callback.
 */
static void GUIEditablePopupMenuDeleteCB(GtkWidget *widget, gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	if(d->freeze_count > 0)
		return;

	if(d->flags & GUI_EDITABLE_POPUP_MENU_READ_ONLY)
		return;

	d->freeze_count++;

	GUIEditablePopupMenuRecordUndo(d);
	gtk_editable_delete_selection(GTK_EDITABLE(d->editable));
	GUIEditablePopupMenuUpdate(d);

	d->freeze_count--;
}

/*
 *	Editable popup menu select all callback.
 */
static void GUIEditablePopupMenuSelectAllCB(GtkWidget *widget, gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	if(d->freeze_count > 0)
		return;

	d->freeze_count++;

	gtk_editable_select_region(GTK_EDITABLE(d->editable), 0, 0);

	gtk_events_process();

	gtk_editable_select_region(GTK_EDITABLE(d->editable), 0, -1);
	GUIEditablePopupMenuUpdate(d);

	d->freeze_count--;
}

/*
 *	Editable popup menu unselect all callback.
 */
static void GUIEditablePopupMenuUnselectAllCB(GtkWidget *widget, gpointer data)
{
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if(d == NULL)
		return;

	if(d->freeze_count > 0)
		return;
			   
	d->freeze_count++;

	gtk_editable_select_region(GTK_EDITABLE(d->editable), 0, 0);
	GUIEditablePopupMenuUpdate(d);

	d->freeze_count--;
}

/*
 *	Editable popup menu GtkEditable event signal callback.
 */
static gint GUIEditablePopupMenuEditableEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	gint status = FALSE;
	GdkEventKey *key;
	GdkEventButton *button;
	GUIEditablePopupMenuData *d = GUI_EDITABLE_POPUP_MENU_DATA(data);
	if((widget == NULL) || (event == NULL) || (d == NULL))
		return(status);

	if(d->freeze_count > 0)
		return(status);

	/* If the event is from a GtkSpinButton then make sure that
	 * the event's GdkWindow is the GtkSpinButton GtkEntry's
	 * text area GdkWindow, this is needed to make sure that the
	 * event did not occure on any of the GtkSpinButton's buttons
	 */
	if(GTK_IS_SPIN_BUTTON(widget))
	{
		GdkEventAny *any = (GdkEventAny *)event;
		GtkEntry *entry = GTK_ENTRY(widget);
		if(entry->text_area != any->window)
			return(status);
	}

	d->freeze_count++;

	switch((gint)event->type)
	{
	  case GDK_KEY_PRESS:
		key = (GdkEventKey *)event;
		if(d->flags & GUI_EDITABLE_POPUP_MENU_UNDO)
		{
			const guint keyval = key->keyval;
			if(isprint(keyval) || (keyval == GDK_BackSpace) ||
			   (keyval == GDK_Delete) || (keyval == GDK_Return) ||
			   (keyval == GDK_KP_Enter) || (keyval == GDK_ISO_Enter)
			)
			{
				GUIEditablePopupMenuRecordUndo(d);
				GUIEditablePopupMenuUpdate(d);
			}
		}
		break;

	  case GDK_BUTTON_PRESS:
		button = (GdkEventButton *)event;
		switch(button->button)
		{
		  case GDK_BUTTON3:
			if(d->menu != NULL)
			{
				/* Update the menu items */
				GUIEditablePopupMenuUpdate(d);

				/* Map the popup menu */
				gtk_menu_popup(  
					GTK_MENU(d->menu),
					NULL, NULL, NULL, d,
					button->button, button->time
				);

				/* Need to mark the GtkText button as 0 or else it
				 * will keep marking
				 */
				if(GTK_IS_TEXT(widget))
				{
					GtkText *text = GTK_TEXT(widget);
					text->button = 0;
					gtk_grab_remove(widget);
					gtk_signal_emit_stop_by_name(
						GTK_OBJECT(widget), "button_press_event"
					);
				}
				else if(GTK_IS_ENTRY(widget))
				{
					GtkEntry *entry = GTK_ENTRY(widget);
					entry->button = 0;
					gtk_grab_remove(widget);
					gtk_signal_emit_stop_by_name(
						GTK_OBJECT(widget), "button_press_event"
					);
				}
				status = TRUE;
			}
			break;
		  case GDK_BUTTON4:
			if(GTK_IS_SPIN_BUTTON(widget))
			{
				GtkEntry *entry = GTK_ENTRY(widget);
				GtkSpinButton *spin = GTK_SPIN_BUTTON(widget);
				GtkAdjustment *adj = gtk_spin_button_get_adjustment(spin);
				gtk_spin_button_spin(
					spin,
					GTK_SPIN_STEP_FORWARD,
					adj->step_increment
				);
				entry->button = 0;
				gtk_grab_remove(widget);
				gtk_signal_emit_stop_by_name(
					GTK_OBJECT(widget), "button_press_event"
				);
				status = TRUE;
			}
			break;
		  case GDK_BUTTON5:
			if(GTK_IS_SPIN_BUTTON(widget))
			{
				GtkEntry *entry = GTK_ENTRY(widget);
				GtkSpinButton *spin = GTK_SPIN_BUTTON(widget);
				GtkAdjustment *adj = gtk_spin_button_get_adjustment(spin);
				gtk_spin_button_spin(
					spin,
					GTK_SPIN_STEP_BACKWARD,
					adj->step_increment
				);
				entry->button = 0;
				gtk_grab_remove(widget);
				gtk_signal_emit_stop_by_name(
					GTK_OBJECT(widget), "button_press_event"
				);
				status = TRUE;
			}
			break;
		}
		break;

	  case GDK_BUTTON_RELEASE:
		button = (GdkEventButton *)event;
		switch(button->button)
		{
		  case GDK_BUTTON3:
			if(d->menu != NULL)
			{
				/* Need to mark the GtkText button as 0 or else it
				 * will keep marking
				 */
				if(GTK_IS_TEXT(widget))
				{
					GtkText *text = GTK_TEXT(widget);
					text->button = 0;
					gtk_grab_remove(widget);
					gtk_signal_emit_stop_by_name(
						GTK_OBJECT(widget), "button_release_event"
					);
				}
				else if(GTK_IS_ENTRY(widget))
				{
					GtkEntry *entry = GTK_ENTRY(widget);
					entry->button = 0;
					gtk_grab_remove(widget);
					gtk_signal_emit_stop_by_name(
						GTK_OBJECT(widget), "button_release_event"
					);
				}
				status = TRUE;
			}
			break;
		  case GDK_BUTTON4:
			if(GTK_IS_SPIN_BUTTON(widget))
			{
				GtkEntry *entry = GTK_ENTRY(widget);
				entry->button = 0;
				gtk_grab_remove(widget);
				gtk_signal_emit_stop_by_name(
					GTK_OBJECT(widget), "button_release_event"
				);
				status = TRUE;
			}
			break;
		  case GDK_BUTTON5:
			if(GTK_IS_SPIN_BUTTON(widget))
			{
				GtkEntry *entry = GTK_ENTRY(widget);
				entry->button = 0;
				gtk_grab_remove(widget);
				gtk_signal_emit_stop_by_name(
					GTK_OBJECT(widget), "button_release_event"
				);
				status = TRUE;
			}
			break;
		}
		break;
	}

	d->freeze_count--;

	return(status);
}


/*
 *	Pullout data destroy callback.
 */
static void GUIPullOutDataDestroyCB(gpointer data)
{
	GtkWidget *client_hbox, *holder_hbox, *toplevel;
	gpointer *cb_data = (gpointer *)data;

	if(cb_data != NULL)
	{
		/* Format is as follows (12 arguments):
		 *
		 *	client_hbox
		 *	holder_hbox
		 *	holder_window
		 *	holder_window_x
		 *	holder_window_y
		 *	holder_window_width
		 *	holder_window_height
		 *	in_place		(1 if true)
		 *	pull_out_client_data
		 *	pull_out_cb
		 *	push_in_client_data
		 *	push_in_cb
		 */
		client_hbox = (GtkWidget *)cb_data[0];
		holder_hbox = (GtkWidget *)cb_data[1];
		toplevel = (GtkWidget *)cb_data[2];

		/* Destroy the toplevel window */
		if(toplevel != NULL)
		{
			GTK_WIDGET_DESTROY(toplevel)
			cb_data[2] = toplevel = NULL;
		}
	}

	g_free(cb_data);
}

/*
 *	Pull out draw handle for signal "draw".
 */
static void GUIPullOutDrawHandleDrawCB(
	GtkWidget *widget, GdkRectangle *area, gpointer data  
)
{
	GdkWindow *window;
	GtkStyle *style;

	if(widget == NULL)
		return;

	if(GTK_WIDGET_NO_WINDOW(widget))
		return;

	window = widget->window;
	style = gtk_widget_get_style(widget);
	if((window == NULL) || (style == NULL))
		return;

	gtk_paint_handle(
		style,
		window,
		GTK_WIDGET_STATE(widget),
		GTK_SHADOW_OUT,
		area,
		widget,
		NULL,				/* No detail */
		0, 0,
		widget->allocation.width,
		widget->allocation.height,
		GTK_ORIENTATION_HORIZONTAL
	);
}

/*
 *	Pull out draw handle callback for signal "expose_event".
 *
 *	This redraws the handle graphics on the given widget.
 */
static gint GUIPullOutDrawHandleCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	if((widget != NULL) ? GTK_WIDGET_NO_WINDOW(widget) : TRUE)
		return(TRUE);
	gtk_widget_queue_draw(widget);
	return(TRUE);
}

/*
 *	Pull out button callback.
 */
static gint GUIPullOutPullOutBtnCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	GtkWidget *client_hbox, *holder_hbox, *toplevel;
	gpointer *cb_data = (gpointer *)data;
	gint holder_window_width, holder_window_height;
	gpointer pull_out_client_data;
	void (*pull_out_cb)(gpointer, gpointer);

	if(cb_data != NULL)
	{
		/* Format is as follows (12 arguments):
		 *
		 *	client_hbox
		 *	holder_hbox
		 *	holder_window
		 *	holder_window_x
		 *	holder_window_y
		 *	holder_window_width
		 *	holder_window_height
		 *	in_place		(1 if true).
		 *	pull_out_client_data
		 *	pull_out_cb
		 *	push_in_client_data
		 *	push_in_cb
		 */
		client_hbox = (GtkWidget *)cb_data[0];
		holder_hbox = (GtkWidget *)cb_data[1];
		toplevel = (GtkWidget *)cb_data[2];

		holder_window_width = (gint)cb_data[5];
		holder_window_height = (gint)cb_data[6];

		pull_out_client_data = cb_data[8];
		pull_out_cb = cb_data[9];

		/* Create toplevel window as needed */
		if(toplevel == NULL)
		{
			GtkWidget *w;

			toplevel = w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
			gtk_window_set_policy(GTK_WINDOW(w), TRUE, TRUE, TRUE);
			cb_data[2] = (gpointer)w;
			gtk_widget_realize(w);
			gtk_widget_set_usize(
				w,
				(holder_window_width <= 0) ? -1 : holder_window_width,
				(holder_window_height <= 0) ? -1 : holder_window_height
			);
			gtk_signal_connect(
				GTK_OBJECT(w), "delete_event",
				GTK_SIGNAL_FUNC(GUIPullOutCloseCB),
				(gpointer)cb_data
			);
		}

		/* Reparent client_hbox to toplevel window */
		if((client_hbox != NULL) && (toplevel != NULL))
		{
			gtk_widget_show(toplevel);

			if((GTK_WIDGET_FLAGS(client_hbox) & GTK_NO_REPARENT))
				g_printerr("Cannot reparent.\n");
			else
				gtk_widget_reparent(client_hbox, toplevel);
		}

		/* Hide holder hbox */
		if(holder_hbox != NULL)
			gtk_widget_hide(holder_hbox);

		/* Mark in callback data that its been `pulled out' */
		cb_data[7] = (gpointer)0;

		/* Call pull out callback? */
		if(pull_out_cb != NULL)
			pull_out_cb(client_hbox, pull_out_client_data);
	}

	return(TRUE);
}

/*
 *	Close (push in) callback.
 */
static gint GUIPullOutCloseCB(GtkWidget *widget, GdkEvent *event, gpointer data)
{
	GtkWidget *client_hbox, *holder_hbox, *toplevel;
	gpointer *cb_data = (gpointer *)data;
	gpointer push_in_client_data;
	void (*push_in_cb)(gpointer, gpointer);

	if(cb_data != NULL)
	{
		/* Format is as follows (12 arguments):
		 *
		 *	client_hbox
		 *	holder_hbox
		 *	holder_window
		 *	holder_window_x
		 *	holder_window_y
		 *	holder_window_width
		 *	holder_window_height
		 *	in_place		(1 if true).
		 *	pull_out_client_data
		 *	pull_out_cb
		 *	push_in_client_data
		 *	push_in_cb
		 */
		client_hbox = (GtkWidget *)cb_data[0];
		holder_hbox = (GtkWidget *)cb_data[1];
		toplevel = (GtkWidget *)cb_data[2];

		push_in_client_data = cb_data[10];
		push_in_cb = cb_data[11];


		/* Reparent client_hbox to holder_hbox */
		if((client_hbox != NULL) && (holder_hbox != NULL))
		{
			gtk_widget_show(holder_hbox);

			if((GTK_WIDGET_FLAGS(client_hbox) & GTK_NO_REPARENT))
				g_printerr("Cannot reparent.\n");
			else
				gtk_widget_reparent(client_hbox, holder_hbox);
		}

		/* Hide toplevel */
		if(toplevel != NULL)
			gtk_widget_hide(toplevel);

		/* Mark in callback data that its been `pushed in' */
		cb_data[7] = (gpointer)1;

		/* Call push in callback? */
		if(push_in_cb != NULL)
			push_in_cb(client_hbox, push_in_client_data);
	}

	return(TRUE);
}


/*
 *	Creates a new GtkRcStyle based on the given GtkRcStyle.
 */
GtkRcStyle *GUIRCStyleCopy(const GtkRcStyle *rcstyle)
{
#if (GTK_MAJOR_VERSION >= 2)
	return((rcstyle != NULL) ? gtk_rc_style_copy(rcstyle) : NULL);
#else
	gint i;
	const GtkRcStyle *src_style = rcstyle;
	GtkRcStyle *tar_style;

	if(src_style == NULL)
		return(NULL);

	tar_style = gtk_rc_style_new();
	tar_style->name = STRDUP(src_style->name);
	tar_style->font_name = STRDUP(src_style->font_name);
	tar_style->fontset_name = STRDUP(src_style->fontset_name);
	for(i = 0; i < 5; i++)
	{
		tar_style->bg_pixmap_name[i] = STRDUP(src_style->bg_pixmap_name[i]);
		tar_style->color_flags[i] = src_style->color_flags[i];
		memcpy(&tar_style->fg[i], &src_style->fg[i], sizeof(GdkColor));
		memcpy(&tar_style->bg[i], &src_style->bg[i], sizeof(GdkColor));
		memcpy(&tar_style->text[i], &src_style->text[i], sizeof(GdkColor));
		memcpy(&tar_style->base[i], &src_style->base[i], sizeof(GdkColor));
	}
	tar_style->engine = src_style->engine;
	tar_style->engine_data = src_style->engine_data;
#endif
	return(tar_style);
}

/*
 *	Sets the widget's rc style and all of its child widgets.
 */
void GUIRCStyleSetRecursive(GtkWidget *w, GtkRcStyle *rcstyle)
{
	if((w == NULL) || (rcstyle == NULL))
		return;

	gtk_widget_modify_style(w, rcstyle);

	if(GTK_IS_CONTAINER(w))
		gtk_container_forall(
			GTK_CONTAINER(w),
			(GtkCallback)GUIRCStyleSetRecursive, rcstyle
		);
}


/*
 *	Parses the geometry string s.
 *
 *	Returns a set of GdkGeometryFlags flags.
 */
GdkGeometryFlags GUIParseGeometry(
	const gchar *s,
	gint *x, gint *y, gint *width, gint *height
)
{
	GdkGeometryFlags status = 0x00000000;

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

	if(s == NULL)
		return(status);

	/* Seek past initial spaces and '=' deliminator (if any) */
	while(ISBLANK(*s))
		s++;
	while(*s == '=')
		s++;
	while(ISBLANK(*s))
		s++;

	/* String at width and height arguments? */
	if((*s != '+') && (*s != '-'))
	{
		/* Parse width value */
		if((width != NULL) && (*s != '\0'))
		{
			*width = ATOI(s);
			status |= GDK_GEOMETRY_WIDTH;
		}

		/* Begin seeking to next argument */
		if(*s != '\0')
			s++;
		while((toupper(*s) != 'X') && (*s != '\0'))
			s++;
		while((toupper(*s) == 'X') || ISBLANK(*s))
			s++;

		/* Parse height value */
		if((height != NULL) && (*s != '\0'))
		{
			*height = ATOI(s);
			status |= GDK_GEOMETRY_HEIGHT;
		}

		/* Begin seeking to next argument (probably an offset
		 * value)
		 */
		if(*s != '\0')
			s++;
		while((*s != '+') && (*s != '-') && (*s != '\0'))
			s++;
	}

	/* String seeked to the offsets arguments? */
	if((*s == '+') || (*s == '-'))
	{
		/* Seek past '+' character as needed and get x offset
		 * value
		 */
		if(*s == '+')
			s++;
		if((x != NULL) && (*s != '\0'))
		{
			*x = ATOI(s);
			status |= GDK_GEOMETRY_X;
		}

		/* Begin seeking to next argument */
		if(*s != '\0')
			s++;
		while((*s != '+') && (*s != '-') && (*s != '\0'))
			s++;

		/* Seek past '+' character as needed and get y offset
		 * value
		 */
		if(*s == '+')
			s++;
		if((y != NULL) && (*s != '\0'))
		{
			*y = ATOI(s);
			status |= GDK_GEOMETRY_Y;
		}
	}

	return(status);
}

/*
 *	Gets the geometry of the GtkCList cell.
 *
 *	The clist specifies the GtkCList.
 *
 *	The column and row specifiy the cell.
 *
 *	The x, y, width, and height specifies the pointers to the
 *	cell geometry return. Any of these values may be NULL if
 *	the value is not requested.
 *
 *	Returns TRUE if the cell geometry was obtained or FALSE on
 *	failed match or error.
 */
gboolean GUICListGetCellGeometry(
	GtkCList *clist,
	const gint column, const gint row,
	gint *x, gint *y, gint *width, gint *height
)
{
	gint i, cx, cy, cwidth, cheight;
	GList *glist;
	const GtkAdjustment *hadj, *vadj;
	const GtkCListRow *row_ptr;
	const GtkCListColumn *column_ptr;

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

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

	hadj = clist->hadjustment;
	vadj = clist->vadjustment;

	/* Given row and column in bounds? */
	if((column < 0) || (column >= clist->columns))
		return(FALSE);
	if((row < 0) || (row >= clist->rows))
		return(FALSE);

	/* Get cy and cheight */
	cy = 0;
	cheight = 0;
	glist = clist->row_list;
	for(i = 0; glist != NULL; i++)
	{
		row_ptr = (const GtkCListRow *)glist->data;
		if(row_ptr != NULL)
		{
			const GtkCellText *cell_text_ptr;
			const GtkCellPixmap *cell_pixmap_ptr;
			const GtkCellPixText *cell_pixtext_ptr;
			const GtkCellWidget *cell_widget_ptr;

			const GtkCell *cell_ptr = row_ptr->cell;
			cheight = clist->row_height;
			if(cell_ptr != NULL)
			{
				switch(cell_ptr->type)
				{
				  case GTK_CELL_TEXT:
					cell_text_ptr = (GtkCellText *)cell_ptr;
					cheight = MAX(cell_text_ptr->vertical, cheight);
					break;
				  case GTK_CELL_PIXMAP:
					cell_pixmap_ptr = (GtkCellPixmap *)cell_ptr;
					cheight = MAX(cell_pixmap_ptr->vertical, cheight);
					break;
				  case GTK_CELL_PIXTEXT:
					cell_pixtext_ptr = (GtkCellPixText *)cell_ptr;
					cheight = MAX(cell_pixtext_ptr->vertical, cheight);
					break;
				  case GTK_CELL_WIDGET:
					cell_widget_ptr = (GtkCellWidget *)cell_ptr;
					cheight = MAX(cell_widget_ptr->vertical, cheight);
					break;
				  case GTK_CELL_EMPTY:
					cheight = 0;
					break;
				}
			}
			cheight += 1;	/* Need to add 1 pixel for cell borders */

			if(i == row)
				break;

			cy += cheight;
		}

		glist = g_list_next(glist);
	}

	/* Get cx and cwidth */
	cx = 0;
	cwidth = 0;
	if(clist->column != NULL)
	{
		for(i = 0; i < clist->columns; i++)
		{
			column_ptr = &clist->column[i];
			if(column_ptr == NULL)
				continue;

			/* Get width of this column plus margins */
			cwidth = column_ptr->width + 7;

			if(i == column)
				break;

			cx += cwidth;
		}
	}

	/* Offset cx and cy with scroll adjustments */
	if(hadj != NULL)
		cx = cx - (gint)(hadj->value - hadj->lower);
	if(vadj != NULL)
		cy = cy - (gint)(vadj->value - vadj->lower);

	/* Update returns */
	if(x != NULL)
		*x = cx;
	if(y != NULL)
		*y = cy;
	if(width != NULL)
		*width = cwidth;
	if(height != NULL)
		*height = cheight;

	return(TRUE);
}


/*
 *	Gets the display.
 */
gpointer GUIGetDisplay(void)
{
	return(gdk_display);
}


/*
 *	Gets the root (desktop) GdkWindow.
 */
GdkWindow *GUIGetWindowRoot(void)
{
	return((GdkWindow *)GDK_ROOT_PARENT());
}

/*
 *	Gets the GdkWindow's coordinates relative to the desktop.
 */
void GUIGetWindowRootPosition(
	GdkWindow *window,
	gint *x_rtn, gint *y_rtn
)
{
	gint cx, cy;
	GdkWindow	*cur_w = window,
					*root_w;

	if(x_rtn != NULL)
		*x_rtn = 0;
	if(y_rtn != NULL)
		*y_rtn = 0;

	if(cur_w == NULL)
		return;

	if(gui_window_root == NULL)
		 gui_window_root = GUIGetWindowRoot();
	root_w = gui_window_root;
	if(root_w == NULL)
		return;

	while(cur_w != root_w)
	{
		gdk_window_get_position(cur_w, &cx, &cy);
		if(x_rtn != NULL)
			*x_rtn += cx;
		if(y_rtn != NULL)
			*y_rtn += cy;
		cur_w = gdk_window_get_parent(cur_w);
	}
}

/*
 *	Converts the GdkWindow's coordinates to be relative to the
 *	desktop.
 */
void GUIGetPositionRoot(
	GdkWindow *w,
	const gint x, const gint y,
	gint *x_rtn, gint *y_rtn
)
{
	gint		_x = x,
					_y = y,
					cx, cy;
	GdkWindow	*cur = w,
					*root_w;

	if(x_rtn != NULL)
		*x_rtn = 0;
	if(y_rtn != NULL)
		*y_rtn = 0;

	if(gui_window_root == NULL)
		 gui_window_root = GUIGetWindowRoot();
	root_w = gui_window_root;

	if((cur == NULL) || (root_w == NULL))
		return;

	while((cur != root_w) && (cur != NULL))
	{
		gdk_window_get_position(cur, &cx, &cy);
		_x += cx;
		_y += cy;
		cur = gdk_window_get_parent(cur);
	}

	if(x_rtn != NULL)
		*x_rtn = _x;
	if(y_rtn != NULL)
		*y_rtn = _y;
}


/*
 *	Gets the GtkCTreeNode's parent.
 */
GtkCTreeNode *GUICTreeNodeGetParent(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr;

	if(node == NULL)
		return(NULL);

	row_ptr = GTK_CTREE_ROW(node);
	if(row_ptr == NULL)
		return(NULL);

	return(row_ptr->parent);
}

/*
 *	Gets the GtkCTreeNode's first child.
 */
GtkCTreeNode *GUICTreeNodeGetChild(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr;

	if(node == NULL)
		return(NULL);

	row_ptr = GTK_CTREE_ROW(node);
	if(row_ptr == NULL)
		return(NULL);

	return(row_ptr->children);
}

/*
 *	Gets the GtkCTreeNode's sibling.
 */
GtkCTreeNode *GUICTreeNodeGetSibling(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr;

	if(node == NULL)
		return(NULL);

	row_ptr = GTK_CTREE_ROW(node);
	if(row_ptr == NULL)
		return(NULL);

	return(row_ptr->sibling);
}

/*
 *	Gets the GtkCTreeNode's expanded flag.
 */
gboolean GUICTreeNodeGetExpanded(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr;

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

	row_ptr = GTK_CTREE_ROW(node);
	if(row_ptr == NULL)
		return(FALSE);

	return(row_ptr->expanded);
}

/*
 *	Gets the row index of the GtkCTreeNode.
 */
gint GUICTreeNodeRow(
	GtkCTree *ctree,
	GtkCTreeNode *node
)
{
	gint row;
	GList *glist;
	GtkCList *clist;
	GtkCTreeRow *row_ptr;

	if((ctree == NULL) || (node == NULL))
		return(-1);

	clist = GTK_CLIST(ctree);

	row_ptr = GTK_CTREE_ROW(node);
	if(row_ptr == NULL)
		return(FALSE);

	/* Count rows until we encounter the specified node's row */
	for(glist = clist->row_list, row = 0;
		glist != NULL;
		glist = g_list_next(glist), row++
	)
	{
		if(row_ptr == (GtkCTreeRow *)glist->data)
			break;
	}

	return((glist != NULL) ? row : -1);
}


/*
 *	Called by GUICTreeNodeDeltaRows() to count the number of rows
 *	to the end node.
 */
static void GUICTreeNodeDeltaRowsIterate(
	GtkCTree *ctree,
	GtkCTreeNode *node,
	GtkCTreeNode *end,
	gint *row_count,
	gboolean *end_found
)
{
	GtkCTreeRow *row_ptr;

	while((node != NULL) && !(*end_found))
	{
		/* Found end node? */
		if(node == end)
		{
			*end_found = TRUE;
			break;
		}

		/* Count this node */
		*row_count = *row_count + 1;

		row_ptr = GTK_CTREE_ROW(node);
		if(row_ptr == NULL)
			break;

		/* Count child nodes only if expanded */
		if(row_ptr->expanded && !row_ptr->is_leaf)
		{
			GUICTreeNodeDeltaRowsIterate(
				ctree, row_ptr->children, end, row_count, end_found
			);
			if(*end_found)
				break;
		}

		/* Get next sibling */
		node = row_ptr->sibling;
	}
}

/*
 *	Gets the number of rows between the GtkCTreeNodes.
 *
 *	The start specifies the starting GtkCTreeNode which may be
 *	NULL for the first toplevel GtkCTreeNode.
 */
gint GUICTreeNodeDeltaRows(
	GtkCTree *ctree,
	GtkCTreeNode *start,
	GtkCTreeNode *end
)
{
	gboolean end_found;
	gint row_count;

	if((ctree == NULL) || (end == NULL))
		return(0);

	if(start == NULL)
		start = gtk_ctree_node_nth(ctree, 0);

	row_count = 0;
	end_found = FALSE;
	GUICTreeNodeDeltaRowsIterate(
		ctree,
		start,
		end,
		&row_count,
		&end_found
	);

	return(row_count);
}


/*
 *	Gets the text bounds.
 */
void GUIGetTextBounds(
	GdkFont *font,
	const gchar *text, const gint text_length,
	GdkTextBounds *bounds
)
{
	if((font == NULL) || (text == NULL) || (bounds == NULL))
	{
		if(bounds != NULL)
			memset(bounds, 0x00, sizeof(GdkTextBounds));
		return;
	}

	gdk_text_extents(
		font, text, text_length,
		&bounds->lbearing, &bounds->rbearing, &bounds->width,
		&bounds->ascent, &bounds->descent
	);
}

/*
 *	Gets the string bounds.
 */
void GUIGetStringBounds(
	GdkFont *font,
	const gchar *string,
	GdkTextBounds *bounds
)
{
	GUIGetTextBounds(font, string, STRLEN(string), bounds);
}


/*
 *	Creates a new GdkGC.
 */
GdkGC *GUIGCNew(GdkWindow *window)
{
	if(window == NULL)
	{
		if(gui_window_root == NULL)
			gui_window_root = GUIGetWindowRoot();
		window = gui_window_root;
	}

	return(gdk_gc_new(window));
}

/*
 *	Unrefs the GdkGC.
 */
GdkGC *GUIGCUnref(GdkGC *gc)
{
	if(gc == NULL)
		return(NULL);

	gdk_gc_unref(gc);

	return(NULL);
}


/*
 *	Blocks input to the toplevel GtkWindow.
 *
 *	The w specifies the toplevel GtkWindow requesting the input
 *	block.
 *
 *	The block specifies whether to block or unblock input.
 */
void GUIBlockInput(
	GtkWidget *w,
	const gboolean block
)
{
//#define USE_GTK_WINDOW_TO_BLOCK
	GtkWidget *bw;				/* Block input GtkWidget */
	GUIBlockInputData *d;

	if(w == NULL)
		return;

	if(!GTK_IS_WINDOW(w))
		return;

	/* Get the block input data from the GtkWindow */
	d = GUI_BLOCK_INPUT_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BLOCK_INPUT_DATA_KEY
	));
	if(d == NULL)
	{
		/* No block input data was set on this GtkWindow so create
		 * and set it for the first time
		 */
		d = GUI_BLOCK_INPUT_DATA(g_malloc(sizeof(GUIBlockInputData)));
		if(d == NULL)
			return;

		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_BLOCK_INPUT_DATA_KEY,
			d, GUIBlockInputDataDestroyCB
		);

#ifdef USE_GTK_WINDOW_TO_BLOCK
		d->invisible = bw = gtk_window_new(GTK_WINDOW_POPUP);
#else
		d->invisible = bw = gtk_invisible_new();
#endif
		d->w = w;
		d->level = 0;

		gtk_widget_set_usize(bw, 1, 1);
		gtk_widget_set_uposition(bw, -1, -1);
		gtk_widget_add_events(
			bw,
			GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
			GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
			GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK |
			GDK_LEAVE_NOTIFY_MASK
		);
		gtk_signal_connect(
			GTK_OBJECT(bw), "key_press_event",
			GTK_SIGNAL_FUNC(GUIBlockInputEventCB), d
		);
		gtk_signal_connect(
			GTK_OBJECT(bw), "key_release_event",
			GTK_SIGNAL_FUNC(GUIBlockInputEventCB), d
		);
		gtk_signal_connect(
			GTK_OBJECT(bw), "button_press_event",
			GTK_SIGNAL_FUNC(GUIBlockInputEventCB), d
		);
		gtk_signal_connect(
			GTK_OBJECT(bw), "button_release_event",
			GTK_SIGNAL_FUNC(GUIBlockInputEventCB), d
		);
		gtk_signal_connect(
			GTK_OBJECT(bw), "motion_notify_event",
			GTK_SIGNAL_FUNC(GUIBlockInputEventCB), d
		);
		gtk_signal_connect(
			GTK_OBJECT(bw), "enter_notify_event",
			GTK_SIGNAL_FUNC(GUIBlockInputEventCB), d
		);
		gtk_signal_connect(
			GTK_OBJECT(bw), "leave_notify_event",
			GTK_SIGNAL_FUNC(GUIBlockInputEventCB), d
		);
	}

	/* Get the GtkInvisible that will be used to grab all input */
	bw = d->invisible;
	if(bw == NULL)
		return;

	/* Block input? */
	if(block)
	{
		/* Increase the block input level and check if it is
		 * the first blocking level
		 */
		d->level++;
		if(d->level == 1)
		{
			/* This is the first blocking level so we
			 * need to map the GtkInvisible and grab it so
			 * that input will be blocked
			 */
#ifdef USE_GTK_WINDOW_TO_BLOCK
			gtk_window_set_modal(GTK_WINDOW(bw), TRUE);
			gtk_widget_show(bw);
#else
			gtk_widget_show(bw);
			if(gtk_grab_get_current() != bw)
				gtk_grab_add(bw);
#endif
		}
	}
	else
	{
		/* Decrease the block input level and check if it is the
		 * last level
		 */
		d->level--;
		if(d->level == 0)
		{
#ifdef USE_GTK_WINDOW_TO_BLOCK
			gtk_window_set_modal(GTK_WINDOW(bw), FALSE);
			gtk_widget_hide(bw);
#else
			if(gtk_grab_get_current() == bw)
				gtk_grab_remove(bw);
			gtk_widget_hide(bw);
#endif
		}

		/* Clip the level */
		if(d->level < 0)
		{
			g_printerr(
"GUIBlockInput(): GtkWidget %p: Block level underflow (level %i).\n",
				w,
				d->level
			);
			d->level = 0;
		}
	}
}

/*
 *	Gets the block input level.
 */
gint GUIBlockInputGetLevel(GtkWidget *w)
{
	GUIBlockInputData *d;

	if(w == NULL)
		return(0);

	d = GUI_BLOCK_INPUT_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BLOCK_INPUT_DATA_KEY
	));
	if(d == NULL)
		return(0);

	return(d->level);
}


/*
 *	Processes GdkEvents by calling gtk_main_iteration() until
 *	there are no more pending GdkEvents.
 *
 *	Warning, time out callbacks and idle callbacks may be
 *	called during this call and produce a race condition. If
 *	the processing time for the callbacks is greater than
 *	the delay between the calls then this function may never
 *	return.
 */
void GUIEventsProcessThrough(void)
{
	while(gtk_events_pending() > 0)
		gtk_main_iteration();
}

/*
 *	Processes only the GdkEvents schedualed at the start of this
 *	call by calling gtk_main_iteration().
 */
void GUIEventsProcessCurrent(void)
{
	gint            i = gtk_events_pending(),
					i2;
	while(i > 0)
	{
		gtk_main_iteration();

		i2 = gtk_events_pending();
		if(i2 <= 0)
			break;

		if(i2 < i)
			i = i2;
		else
			i--;
	}
}

/*
 *	Processes GdkEvents by calling gtk_main_iteration() until
 *	there are no more pending GdkEvents or the time limit has
 *	been reached.
 *
 *	The limit_ms specifies the time limit in milliseconds.
 */
void GUIEventsProcessUntil(const gulong limit_ms)
{
	gint            i = gtk_events_pending(),
					i2;
	GTimer *timer = NULL;
	while(i > 0)
	{
		gtk_main_iteration();

		i2 = gtk_events_pending();
		if(i2 <= 0)
			break;

		/* More events have arrived than the number calculated to
		 * have been processed based on the number of events at
		 * the start of this call?
		 */
		if((i - i2) <= 0)
		{
			/* If a timer has been schedualed then check if we
			 * have exceeded the specified time limit
			 */
			if(timer != NULL)
			{
				if(G_TIMER_ELAPSED_MS(timer) > limit_ms)
					break;
			}
			else
			{
				/* No timer schedualed yet so schedual one now */
				timer = g_timer_new();
				g_timer_start(timer);
			}
		}

		i = i2;
	}
	G_TIMER_DESTROY(timer);
}


/*
 *	Removes the timeout callback.
 */
guint GUITimeoutRemove(const guint id)
{
	if(id == 0)
		return(0);

	gtk_timeout_remove(id);

	return(0);
}

/*
 *	Removes the idle callback.
 */
guint GUIIdleRemove(const guint id)
{
	if(id == 0)
		return(0);

	gtk_idle_remove(id);

	return(0);
}


/*
 *	Gets the GdkWindow's reference count.
 */
gint GUIWindowGetRefCount(GdkWindow *window)
{
#if defined(_WIN32)
	return((window != NULL) ? 1 : 0);
#else
	return((window != NULL) ?
		((GdkWindowPrivate *)window)->ref_count : 0
	);
#endif
}


/*
 *	Sets the toplevel GdkWindow's WM (Window Manager) icon.
 */
void GUISetWMIcon(
	GdkWindow *window,
	guint8 **data
)
{
	GdkBitmap *mask;
	GdkPixmap *pixmap;

	if((window == NULL) || (data == NULL))
		return;

	/* Load the icon pixmap and mask */
	pixmap = GUIPixmapNewFromXPMData(
		window,
		&mask,
		NULL,				/* No background color */
		data
	);
	if(pixmap == NULL)
		return;

	/* Set the GdkWindow's WM icon
	 *
	 * gdk_window_set_icon() will unref() the pixmap and mask
	 */
	gdk_window_set_icon(
		window,
		NULL,				/* No icon GdkWindow */
		pixmap, mask
	);
}

/*
 *	Sets the WM icon for the GdkWindow from an XPM file.
 */
void GUISetWMIconFile(
	GdkWindow *window,
	const gchar *path
)
{ 
	GdkBitmap *mask;
	GdkPixmap *pixmap;

	if((window == NULL) || STRISEMPTY(path))
		return;

	/* Load icon pixmap and mask */
	pixmap = GUIPixmapNewFromXPMFile(
		window,
		&mask,
		NULL,
		path
	);
	if(pixmap == NULL)
		return;

	/* Set the GdkWindow's WM icon
	 *
	 * gdk_window_set_icon() will unref() the pixmap and mask
	 */
	gdk_window_set_icon(
		window,
		NULL,				/* No icon GdkWindow */
		pixmap,
		mask
	);
}


/*
 *	Applies the command line arguments to the GtkWindow.
 *
 *	Command line arguments that will be applied are as follows:
 *
 *	--title <title>			Set WM title bar title
 *	--class <class>			Set WM class
 *	--icon <filename>		Set WM icon
 *	--icon-name <name>		Set WM icon's name
 *	--font-name <font>		Set font
 *	--font
 *	-fn
 *	--foreground-color <color>	Set foreground color
 *	-fg
 *	--background-color <color>	Set background color
 *	-bg
 *	--text-color <color>		Set text color
 *	--base-color <color>		Set base color
 *	--background-pixmap <filename>	Set background pixmap
 *	--bg-pixmap
 *	--sforeground-color <color>	Set selected foreground color
 *	-sfg
 *	--sbackground-color <color>	Set selected background color
 *	-sbg
 *	--stext-color <color>		Set selected text color
 *	--sbase-color <color>		Set selected base color
 *	--sbackground-pixmap <filename>	Set selected background pixmap
 *	--sbg-pixmap
 *	--geometry <geometry>		Set geometry
 *	--iconic			Startup iconified
 *
 */
void GUIWindowApplyArgs(
	GtkWindow *w,
	const gint argc, gchar **argv
)
{
	gint i;
	gboolean arg_on;
	const gchar *arg, *argp;
	gchar		*title = NULL,
					*wmclass = NULL,
					*icon = NULL,
					*icon_name = NULL;
	GdkGeometryFlags geometry_flags = 0x00000000;
	GdkRectangle	*geometry = NULL;
	gboolean	iconic = FALSE;
	GtkRcStyle	*rcstyle = NULL;
	GdkWindow *window;
	GtkWidget *widget = (GtkWidget *)w;

#define NEW_RCSTYLE_AS_NEEDED	{	\
if(rcstyle == NULL)			\
 rcstyle = gtk_rc_style_new();		\
}

	if((w == NULL) || (argc <= 0) || (argv == NULL))
		return;

	if(!GTK_IS_WINDOW(widget))
		return;

	/* Must realize the GtkWindow so that we can set its GdkWindow */
	if(!GTK_WIDGET_REALIZED(widget))
		gtk_widget_realize(widget);

	window = widget->window;
	if(window == NULL)
		return;

	/* Iterate through arguments and gather values to be applied to
	 * the GtkWindow
	 */
	for(i = 0; i < argc; i++)
	{
		arg = argv[i];
		if(STRISEMPTY(arg))
			continue;

		/* If the argument does not start with a '-' character then
		 * we can skip the checking of the argument for convience
		 */
		if((*arg != '-') && (*arg != '+'))
			continue;

		/* If arg is prefixed with a '+' character then set arg_on
		 * to TRUE so that toggle arguments can know to enable or
		 * disable
		 */
		arg_on = (*arg == '+') ? TRUE : FALSE;

		/* Get argp to seek past the '-' characters in arg */
		argp = arg;
		while((*argp == '-') || (*argp == '+'))
			argp++;

#define PRINT_REQUIRES_ARG(_a_)	{	\
 g_printerr(				\
  "%s: Requires argument.\n",		\
  (_a_)					\
 );					\
}

		/* Handle by argument */

		/* Title */
		if(!g_strcasecmp(argp, "title"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				g_free(title);
				title = STRDUP(arg);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Class */
		else if(!g_strcasecmp(argp, "class"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				g_free(wmclass);
				wmclass = STRDUP(arg);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Icon */
		else if(!g_strcasecmp(argp, "icon"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				g_free(icon);
				icon = STRDUP(arg);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Icon Name */
		else if(!g_strcasecmp(argp, "icon-name"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				g_free(icon_name);
				icon_name = STRDUP(arg);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Font */
		else if(!g_strcasecmp(argp, "font-name") ||
				!g_strcasecmp(argp, "font") ||
				!g_strcasecmp(argp, "fn")
		)
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				NEW_RCSTYLE_AS_NEEDED
				g_free(rcstyle->font_name);
				rcstyle->font_name = STRDUP(arg);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Foreground Color */
		else if(!g_strcasecmp(argp, "foreground-color") ||
				!g_strcasecmp(argp, "foreground") ||
				!g_strcasecmp(argp, "fg")
		)
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_NORMAL;
				NEW_RCSTYLE_AS_NEEDED
				rcstyle->color_flags[state] |= GTK_RC_FG;
				c = &rcstyle->fg[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Background Color */
		else if(!g_strcasecmp(argp, "background-color") ||
				!g_strcasecmp(argp, "background") ||
				!g_strcasecmp(argp, "bg")
		)
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_NORMAL;
				NEW_RCSTYLE_AS_NEEDED
				rcstyle->color_flags[state] |= GTK_RC_BG;
				c = &rcstyle->bg[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Text Color */
		else if(!g_strcasecmp(argp, "text-color"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_NORMAL;
				NEW_RCSTYLE_AS_NEEDED
				rcstyle->color_flags[state] |= GTK_RC_TEXT;
				c = &rcstyle->text[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Base Color */
		else if(!g_strcasecmp(argp, "base-color"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_NORMAL;
				NEW_RCSTYLE_AS_NEEDED
				rcstyle->color_flags[state] |= GTK_RC_BASE;
				c = &rcstyle->base[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Background Pixmap */
		else if(!g_strcasecmp(argp, "background-pixmap") ||
				!g_strcasecmp(argp, "bg-pixmap")
		)
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GtkStateType state = GTK_STATE_NORMAL;
				NEW_RCSTYLE_AS_NEEDED
				g_free(rcstyle->bg_pixmap_name[state]);
				rcstyle->bg_pixmap_name[state] = STRDUP(arg);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Selected Foreground Color */
		else if(!g_strcasecmp(argp, "sforeground-color") ||
				!g_strcasecmp(argp, "sforeground") ||
				!g_strcasecmp(argp, "sfg")
		)
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_SELECTED;
				NEW_RCSTYLE_AS_NEEDED
				rcstyle->color_flags[state] |= GTK_RC_FG;
				c = &rcstyle->fg[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Selected Background Color */
		else if(!g_strcasecmp(argp, "sbackground-color") ||
				!g_strcasecmp(argp, "sbackground") ||
				!g_strcasecmp(argp, "sbg")
		)
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_SELECTED;
				NEW_RCSTYLE_AS_NEEDED                  
				rcstyle->color_flags[state] |= GTK_RC_BG;
				c = &rcstyle->bg[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Selected Text Color */
		else if(!g_strcasecmp(argp, "stext-color"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_SELECTED;
				NEW_RCSTYLE_AS_NEEDED
				rcstyle->color_flags[state] |= GTK_RC_TEXT;
				c = &rcstyle->text[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Selected Base Color */
		else if(!g_strcasecmp(argp, "sbase-color"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GdkColor *c;
				GtkStateType state = GTK_STATE_SELECTED;
				NEW_RCSTYLE_AS_NEEDED
				rcstyle->color_flags[state] |= GTK_RC_BASE;
				c = &rcstyle->base[state];
				gdk_color_parse(arg, c);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Selected Background Pixmap */
		else if(!g_strcasecmp(argp, "sbackground-pixmap") ||
				!g_strcasecmp(argp, "sbg-pixmap")
		)
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				GtkStateType state = GTK_STATE_SELECTED;
				NEW_RCSTYLE_AS_NEEDED
				g_free(rcstyle->bg_pixmap_name[state]);
				rcstyle->bg_pixmap_name[state] = STRDUP(arg);
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}

		/* Geometry */
		else if(!g_strcasecmp(argp, "geometry"))
		{
			i++;
			arg = (i < argc) ? argv[i] : NULL;
			if(arg != NULL)
			{
				gint x, y, width, height;
				const GdkGeometryFlags flags = gdk_parse_geometry(
					arg,
					&x, &y,
					&width, &height
				);
				geometry_flags = flags;
				if(flags != 0)
				{
					GdkRectangle *rect = geometry;
					if(rect == NULL)
						geometry = rect = (GdkRectangle *)g_malloc0(
							sizeof(GdkRectangle)
						);
					rect->x = (flags & GDK_GEOMETRY_X) ? x : 0;
					rect->y = (flags & GDK_GEOMETRY_Y) ? y : 0;
					rect->width = (flags & GDK_GEOMETRY_WIDTH) ? width : 0;
					rect->height = (flags & GDK_GEOMETRY_HEIGHT) ? height : 0;
				}
			}
			else
			{
				PRINT_REQUIRES_ARG(argv[i - 1])
			}
		}
		/* Iconic */
		else if(!g_strcasecmp(argp, "iconic"))
		{
			iconic = TRUE;
		}
#undef PRINT_REQUIRES_ARG
	}


	/* Begin applying values */

	/* Set title? */
	if(title != NULL)
	{
		gtk_window_set_title(w, title);
	}

	/* Set class? */
	if(wmclass != NULL)
	{
#if 0
TODO: Should not set this after GtkWindow has been realized
		gtk_window_set_wmclass(w, wmclass, wmclass);
#endif
	}

	/* Set icon? */
	if(icon != NULL)
	{
		GUISetWMIconFile(
			window,
			icon
		);
	}

	/* Set icon name? */
	if(icon_name != NULL)
	{
		gdk_window_set_icon_name(window, icon_name);
	}

	/* Set geometry? */
	if((geometry_flags != 0) && (geometry != NULL))
	{
		const GdkGeometryFlags flags = geometry_flags;
		const GdkRectangle *rect = geometry;
		if((flags & GDK_GEOMETRY_WIDTH) || (flags & GDK_GEOMETRY_HEIGHT))
			gtk_widget_set_usize(
				GTK_WIDGET(w),
				(flags & GDK_GEOMETRY_WIDTH) ? rect->width : -1,
				(flags & GDK_GEOMETRY_HEIGHT) ? rect->height : -1
			);
		if((flags & GDK_GEOMETRY_X) || (flags & GDK_GEOMETRY_Y))
			gtk_widget_set_uposition(
				GTK_WIDGET(w),
				(flags & GDK_GEOMETRY_X) ? rect->x : 0,
				(flags & GDK_GEOMETRY_Y) ? rect->y : 0
			);
	}

	/* Iconic? */
	if(iconic)
	{
/* TODO */

	}

	/* Set rc style? */
	if(rcstyle != NULL)
	{
		gtk_widget_modify_style_recursive(
			GTK_WIDGET(w), rcstyle
		);
	}

	g_free(title);
	g_free(wmclass);
	g_free(icon);
	g_free(icon_name);
	GTK_RC_STYLE_UNREF(rcstyle);
	g_free(geometry);

#undef NEW_RCSTYLE_AS_NEEDED
}


/*
 *	Checks if the argument is an argument that is handled by
 *	GUIWindowApplyArgs.
 */
gboolean GUIIsWindowArg(const gchar *arg)
{
	if(arg == NULL)
		return(FALSE);

	if(*arg != '-')
		return(FALSE);

	/* Seek past the initial '-' characters */
	while(*arg == '-')
		arg++;

	if(!g_strcasecmp(arg, "title") ||
	   !g_strcasecmp(arg, "class") ||
	   !g_strcasecmp(arg, "icon") ||
	   !g_strcasecmp(arg, "icon-name") ||
	   !g_strcasecmp(arg, "font-name") ||
	   !g_strcasecmp(arg, "font") ||
	   !g_strcasecmp(arg, "fn") ||
	   !g_strcasecmp(arg, "foreground-color") ||
	   !g_strcasecmp(arg, "fg") ||
	   !g_strcasecmp(arg, "background-color") ||
	   !g_strcasecmp(arg, "bg") ||
	   !g_strcasecmp(arg, "text-color") ||
	   !g_strcasecmp(arg, "base-color") ||
	   !g_strcasecmp(arg, "background-pixmap") ||
	   !g_strcasecmp(arg, "bg-pixmap") ||
	   !g_strcasecmp(arg, "sforeground-color") ||
	   !g_strcasecmp(arg, "sfg") ||
	   !g_strcasecmp(arg, "sbackground-color") ||
	   !g_strcasecmp(arg, "sbg") ||
	   !g_strcasecmp(arg, "stext-color") ||
	   !g_strcasecmp(arg, "sbase-color") ||
	   !g_strcasecmp(arg, "sbackground-pixmap") ||
	   !g_strcasecmp(arg, "sbg-pixmap") ||
	   !g_strcasecmp(arg, "geometry") ||
	   !g_strcasecmp(arg, "iconic")
	)
		return(TRUE);
	else
		return(FALSE);
}


/*
 *	Sets the GtkCTree's position for optimul viewing of all the
 *	nodes starting from the expanded node.
 *
 *	The node specifies the expanded parent node.
 *
 *	Returns TRUE if the position has changed.
 */
gboolean GUICTreeOptimizeExpandPosition(
	GtkCTree *ctree,
	GtkCTreeNode *node
)
{
	gboolean need_scroll = FALSE;
	gint row_height, rows_visible, nchild_rows;
/*	GtkAdjustment *vadj; */
	GtkCList *clist;
	GtkCTreeRow *row_ptr;
	GtkCTreeNode *cur_node, *last_child_node, *scroll_end_node = NULL;

	if((ctree == NULL) || (node == NULL))
		return(FALSE);

	/* Get the clist information */
	clist = GTK_CLIST(ctree);
	if(clist->columns <= 0)
		return(FALSE);

#if 0
	vadj = clist->vadjustment;
	if(vadj == NULL)
		return(FALSE);
#endif

	/* Get the row height if it is set and assume uniform row
	 * spacing, otherwise assume the default to be 20
	 */
	if(GTK_CLIST_ROW_HEIGHT_SET(clist))
		row_height = clist->row_height + 1;
	else
		row_height = 20;
	if(row_height <= 0)
		return(FALSE);

	/* Calculate the number of rows visible */
	rows_visible = clist->clist_window_height / row_height;
	if(rows_visible <= 0)
		return(FALSE);

	/* Get the first child of the expanded node */
	last_child_node = NULL;
	nchild_rows = 1;		/* Include the parent */
	row_ptr = GTK_CTREE_ROW(node);
	cur_node = (row_ptr != NULL) ? row_ptr->children : NULL;
	while(cur_node != NULL)
	{
		last_child_node = cur_node;
		nchild_rows++;
		if(nchild_rows > rows_visible)
		{
			need_scroll = TRUE;
			break;
		}

		row_ptr = GTK_CTREE_ROW(cur_node);
		cur_node = (row_ptr != NULL) ? row_ptr->sibling : NULL;
	}

	/* Check if last child node is not visible */
	if(!need_scroll && (last_child_node != NULL))
	{
#if 1
		const gint	branch_start_rows = GUICTreeNodeDeltaRows(
			ctree, NULL, node
		),
					branch_end_rows = branch_start_rows + nchild_rows;
		GtkCTreeNode *end_node = gtk_ctree_node_nth(
			ctree, branch_end_rows - 1
		);
		if(end_node != NULL)
		{
			if(gtk_ctree_node_is_visible(
				ctree, end_node
			) != GTK_VISIBILITY_FULL)
			{
				/* It is not visible so mark that we need to scroll
				 * and get the end node
				 */
				need_scroll = TRUE;
				scroll_end_node = end_node;
			}
		}
#else
		const gint	branch_start_rows = GUICTreeNodeDeltaRows(
			ctree, NULL, node
		),
					branch_end_rows = branch_start_rows + nchild_rows;
		const gint	y_scroll_pos = (gint)MAX(vadj->value, vadj->lower),
					height = MAX(clist->clist_window_height, 0),
					y = (branch_end_rows * row_height) - y_scroll_pos;
		if(y > height)
		{
			/* It is not visible so mark that we need to scroll
			 * and get the end node
			 */
			need_scroll = TRUE;
			scroll_end_node = gtk_ctree_node_nth(ctree, branch_end_rows - 1);
		}
#endif
	}

	/* Need to scroll? */
	if(need_scroll)
	{
		if(scroll_end_node != NULL)
			gtk_ctree_node_moveto(
				ctree,
				scroll_end_node, -1,
				1.0f, 0.0f	/* Row align, column align */
			);
		else
			gtk_ctree_node_moveto(
				ctree,
				node, -1,
				0.0f, 0.0f	/* Row align, column align */
			);
		return(TRUE);
	}
	else
	{
		return(FALSE);
	}
}


/*
 *	Unrefs the GdkFont.
 */
GdkFont *GUIFontUnref(GdkFont *font)
{
	if(font == NULL)
		return(NULL);

	gdk_font_unref(font);

	return(NULL);
}


/*
 *	Unrefs the GdkColormap.
 */
GdkColormap *GUIColormapUnref(GdkColormap *colormap)
{
	if(colormap == NULL)
		return(NULL);

	gdk_colormap_unref(colormap);

	return(NULL);
}


/*
 *	Creates a new GdkBitmap.
 */
GdkBitmap *GUIBitmapNew(
	GdkWindow *window,
	const gint width, const gint height
)
{
	if((width <= 0) || (height <= 0))
		return(NULL);

	if(window == NULL)
	{
		if(gui_window_root == NULL)
			gui_window_root = GUIGetWindowRoot();
		window = gui_window_root;
	}

	return(gdk_pixmap_new(
		window,
		width, height,
		1
	));
}

/*
 *	Creates a new GdkBitmap from RGBA image data using its alpha
 *	channel.
 *
 *	The width and height specifies the size of the RGBA image data
 *	and the size of the new GdkBitmap.
 *
 *	The bpl specifies the bytes per line of the RGBA image data.
 *
 *	The rgba specifies the RGBA image data.
 *
 *	The threshold specifies the alpha channel threshold, any alpha
 *	channel value greater than or equal to threshold will be
 *	converted to a 1 bit on the GdkBitmap and any alpha channel
 *	value less than threshold will be converted to a 0 but on the
 *	GdkBitmap.
 *
 *	The window specifies the GdkWindow to create the GdkBitmap.
 *	The GdkWindow may not be a GdkPixmap or GdkBitmap. If window
 *	is NULL then the root GdkWindow will be used.
 */
GdkBitmap *GUICreateBitmapFromDataRGBA(
	const gint width, const gint height,
	const gint bpl,
	const guint8 *rgba,
	const guint8 threshold,
	GdkWindow *window
)
{
	const gint bpp = 4;			/* RGBA */
	gint		x, y,
					xbm_bpl,
					src_bpl = bpl,
					tar_bpl;
	const guint8	*src_data,
					*src_ptr;
	guint8		*xbm_data,
					*tar_data,
					*tar_row;
	GdkBitmap *bitmap;

	if(gui_window_root == NULL)
		 gui_window_root = GUIGetWindowRoot();

	if(window == NULL)
		window = gui_window_root;

	if((rgba == NULL) || (width <= 0) || (height <= 0))
		return(NULL);

	if(src_bpl <= 0)
		src_bpl = width * bpp;
	if(src_bpl <= 0)
		return(NULL);

	/* Calculate the number of bytes that are needed to hold
	 * one row of bits for the bitmap data
	 */
	xbm_bpl = (width / 8) + (((width % 8) != 0) ? 1 : 0);

	/* Allocate the bitmap data */
	xbm_data = (guint8 *)g_malloc0(
		xbm_bpl * height * sizeof(guint8)
	);
	if(xbm_data == NULL)
		return(NULL);

	/* Convert the RGBA data to the bitmap data */
	src_data = rgba;
	tar_data = xbm_data;
	tar_bpl = xbm_bpl;
	for(y = 0; y < height; y++)
	{
		src_ptr = src_data + (y * src_bpl);
		tar_row = tar_data + (y * tar_bpl);
		for(x = 0; x < width; x++)
		{
			if(src_ptr[3] >= threshold)
				tar_row[(x / 8)] |= (guint8)(1 << (x % 8));
			src_ptr += bpp;
		}
	}

	/* Create the GdkBitmap from the bitmap data */
	bitmap = gdk_bitmap_create_from_data(
		window,
		(const gchar *)xbm_data,
		width, height
	);

	/* Delete the bitmap data */
	g_free(xbm_data);

	return(bitmap);
}

/*
 *	Unrefs the GdkBitmap.
 */
GdkBitmap *GUIBitmapUnref(GdkBitmap *bitmap)
{
	if(bitmap == NULL)
		return(NULL);

	gdk_bitmap_unref(bitmap);

	return(NULL);
}


/*
 *	Creates a new GdkPixmap.
 */
GdkPixmap *GUIPixmapNew(
	GdkWindow *window,
	const gint width, const gint height,
	const gint depth
)
{
	if((width <= 0) || (height <= 0))
		return(NULL);

	if(window == NULL)
	{
		if(gui_window_root == NULL)
			gui_window_root = GUIGetWindowRoot();
		window = gui_window_root;
	}

	return(gdk_pixmap_new(
		window,
		width, height,
		-1
	));
}

/*
 *	Creates a new GdkPixmap from XPM data.
 */
GdkPixmap *GUIPixmapNewFromXPMData(
	GdkWindow *window,
	GdkBitmap **mask_rtn,
	GdkColor *bg_color,
	guint8 **data
)
{
	GdkPixmap *pixmap;
	GdkBitmap *mask;

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

	if(data == NULL)
		return(NULL);

	if(window == NULL)
	{
		if(gui_window_root == NULL)
			gui_window_root = GUIGetWindowRoot();
		window = gui_window_root;
	}

	/* Create a new GdkPixmap from the XPM data */
	pixmap = gdk_pixmap_create_from_xpm_d(
		window,
		&mask,
		bg_color,
		(gchar **)data
	);
	if(pixmap == NULL)
		return(NULL);

	if(mask_rtn != NULL)
		*mask_rtn = mask;
	else
		GDK_BITMAP_UNREF(mask);

	return(pixmap);
}

/*
 *	Creates a new GdkPixmap from an XPM file.
 */
GdkPixmap *GUIPixmapNewFromXPMFile(
	GdkWindow *window,
	GdkBitmap **mask_rtn,
	GdkColor *bg_color,
	const gchar *path
)
{
	GdkBitmap *mask;
	GdkPixmap *pixmap;

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

	if(STRISEMPTY(path))
		return(NULL);

	if(window == NULL)
	{
		if(gui_window_root == NULL)
			gui_window_root = GUIGetWindowRoot();
		window = gui_window_root;
	}

	/* Create a new GdkPixmap from the XPM file */
	pixmap = gdk_pixmap_create_from_xpm(
		window,
		&mask,
		bg_color,
		path
	);
	if(pixmap == NULL)
		return(NULL);

	if(mask_rtn != NULL)
		*mask_rtn = mask;
	else
		GDK_BITMAP_UNREF(mask);

	return(pixmap);
}

/*
 *	Creates a new GtkPixmap from XPM data.
 */
GtkWidget *GUIGtkPixmapNewFromXPMData(
	GdkWindow *window,
	GdkColor *bg_color,
	guint8 **data
)
{
	GdkBitmap *mask;
	GdkPixmap *pixmap;
	GtkWidget *w;

	/* Create a new GdkPixmap from the XPM data */
	pixmap = GUIPixmapNewFromXPMData(
		window,
		&mask,
		bg_color,
		data
	);
	if(pixmap == NULL)
		return(NULL);

	/* Create a new GtkPixmap from the GdkPixmap */
	w = gtk_pixmap_new(pixmap, mask);

	GDK_PIXMAP_UNREF(pixmap);
	GDK_BITMAP_UNREF(mask);

	return(w);
}

/*
 *	Creates a new GtkPixmap from XPM data.
 */
GtkWidget *GUICreateMenuItemIcon(guint8 **icon_data)
{
	GtkWidget *w = NULL;

	if(icon_data != NULL)
	{
		/* Create a new GdkPixmap from XPM data */
		GdkBitmap *mask;
		GdkPixmap *pixmap = GUIPixmapNewFromXPMData(
		    NULL,
			&mask,
			NULL,
		    icon_data
		);
		if(pixmap != NULL)
		{
			w = gtk_pixmap_new(pixmap, mask);

			GDK_PIXMAP_UNREF(pixmap);
			GDK_BITMAP_UNREF(mask);
		}
	}
	else
	{
		const gint	width = GUI_MENU_ITEM_DEF_HEIGHT,
					height = GUI_MENU_ITEM_DEF_HEIGHT;
		gchar *bitmap_data;
		GdkBitmap *mask;
		GdkPixmap *pixmap;
		GdkWindow *window;

		if(gui_window_root == NULL)
			gui_window_root = GUIGetWindowRoot();
		window = gui_window_root;

		/* Create an empty GdkBitmap */
		bitmap_data = (gchar *)g_malloc0(
			(((width / 8) + 1) * height) * sizeof(gchar)
		);
		if(bitmap_data != NULL)
		{
			mask = gdk_bitmap_create_from_data(
				window,
				bitmap_data,
				width, height
			);
			g_free(bitmap_data);
		}
		else
		{
			mask = NULL;
		}

		/* Create an empty GdkPixmap */
		pixmap = GUIPixmapNew(
			window,
			width, height,
			-1
		);
		if(pixmap != NULL)
		{
			w = gtk_pixmap_new(pixmap, mask);

			GDK_PIXMAP_UNREF(pixmap);
			GDK_BITMAP_UNREF(mask);
		}
	}

	return(w);
}

/*
 *	Unrefs the GdkPixmap.
 */
GdkPixmap *GUIPixmapUnref(GdkPixmap *pixmap)
{
	if(pixmap == NULL)
		return(NULL);

	gdk_pixmap_unref(pixmap);

	return(NULL);
}


/*
 *	Adds a ref count to the GtkObject.
 */
GtkObject *GUIObjectRef(GtkObject *o)
{
	if(o == NULL)
		return(NULL);

	gtk_object_ref(o);

	return(o);
}

/*
 *	Unrefs the GtkObject.
 */
GtkObject *GUIObjectUnref(GtkObject *o)
{
	if(o == NULL)
		return(NULL);

	gtk_object_unref(o);

	return(NULL);
}


/*
 *	Sets the GtkAdjutment's value.
 *
 *	The adj specifies the GtkAdjustment.
 *
 *	The v specifies the value, this value will be clamped to the
 *	adjustment's range.
 *
 *	If emit_value_changed is TRUE then a "value_changed" signal
 *	will be emitted.
 */
void GUIAdjustmentSetValue(
	GtkAdjustment *adj,
	const gfloat v,
	const gboolean emit_value_changed
)
{
	gfloat _v = v;

	if(adj == NULL)
		return;

	/* Clamp the value */
	if(_v > (adj->upper - adj->page_size))
		_v = adj->upper - adj->page_size;
	if(_v < adj->lower)
		_v = adj->lower;

	/* Change in value? */
	if(adj->value != _v)
	{
		/* Set the new value */
		adj->value = _v;

		if(emit_value_changed)
			gtk_signal_emit_by_name(
				GTK_OBJECT(adj),
				"value_changed"
			);
	}
}


/*
 *	Maps and raises the GtkWidget.
 *
 *	If the GtkWidget is a GtkWindow then it will be raised.
 */
void GUIWidgetMapRaise(GtkWidget *w)
{
	if(w == NULL)
		return;

	gtk_widget_show(w);
	if(GTK_IS_WINDOW(w))
		gdk_window_raise(w->window);
}

/*
 *	Minimizes (iconifies) the GtkWindow.
 */
void GUIWindowMinimize(GtkWindow *window)
{
#ifdef _XLIB_H_
	Display *dpy;
	Window xwin;
	GdkWindow *win;
	gint scr_num;

	if(window == NULL)
		return;

	if(!GTK_WIDGET_REALIZED(GTK_WIDGET(window)))
		return;

	win = GTK_WIDGET(window)->window;
	if(win == NULL)
		return;

	dpy = GDK_WINDOW_XDISPLAY(win);
	xwin = GDK_WINDOW_XWINDOW(win);
	if((dpy == NULL) || (xwin == None))
		return;

	scr_num = DefaultScreen(dpy);
	XIconifyWindow(dpy, xwin, scr_num);

#endif	/* _XLIB_H_ */
}

/*
 *	Creates the global gui_tooltips widget for controlling the
 *	enabled/disabled state of all tooltips set by these functions.
 *
 *	When the global gui_tooltips widget is first created, the
 *	tooltips will be enabled and gui_tooltips_state will be reset
 *	to TRUE.
 */
static void GUICreateGlobalTooltips(void)
{
	/* Need to create global gui_tooltips widget? */
	if(gui_tooltips == NULL)
	{
		GtkWidget *w = (GtkWidget *)gtk_tooltips_new();
		gui_tooltips = (GtkTooltips *)w;
		if(w != NULL)
		{
			gtk_tooltips_enable(GTK_TOOLTIPS(w));
			gui_tooltips_state = TRUE;
		}
	}
}

/*
 *	Sets the widget's tooltip.
 *
 *	The w specifies the GtkWidget.
 *
 *	The tip specifies the string describing the tooltip. If tip
 *	is NULL then the widget's tooltip will be cleared.
 */
void GUISetWidgetTip(
	GtkWidget *w,
	const gchar *tip
)
{
	if(w == NULL)
		return;

	/* Create the global GtkTooltips as needed */
	GUICreateGlobalTooltips();
	if(gui_tooltips == NULL)
		return;

	gtk_tooltips_set_tip(
		gui_tooltips,
		w,
		tip,
		NULL				/* No private tip */
	);
}

/*
 *	Makes the tooltip associated with the given widget shown
 *	immediatly if the widget has a tip defined. Also requires
 *	that the global gui_tooltips_state be TRUE, otherwise
 *	this function does nothing.
 */
void GUIShowTipsNow(GtkWidget *w)
{
	GdkEventCrossing e;
	GdkWindow *window;

	if((w == NULL) || !gui_tooltips_state)
		return;

	if(GTK_WIDGET_NO_WINDOW(w))
		return;

	window = w->window;
	if(window == NULL)
		return;

	/* Send a fake enter notify event to make widget think
	 * its time to show the tooltips. Note that the widget
	 * should be watching for the enter_notify_event.
	 */
	e.type = GDK_ENTER_NOTIFY;
	e.window = window;
	e.send_event = 1;		/* True if we're sending event */
	e.subwindow = window;
	e.time = GDK_CURRENT_TIME;
	e.x = 0.0;
	e.y = 0.0;
	e.x_root = 0.0;
	e.y_root = 0.0;
	e.mode = GDK_CROSSING_NORMAL;
	e.detail = GDK_NOTIFY_ANCESTOR;
	e.focus = TRUE;			/* Focus */
	e.state = 0;			/* Key modifiers */
	gdk_event_put((GdkEvent *)&e);
}

/*
 *	Enables/disables the global tooltips state.
 */
void GUISetGlobalTipsState(const gboolean state)
{
	/* Create global tooltips as needed */
	GUICreateGlobalTooltips();
	if(gui_tooltips == NULL)
		return;

	if(state)
		gtk_tooltips_enable(gui_tooltips);
	else
		gtk_tooltips_disable(gui_tooltips);

	/* Update global tool tips state */
	gui_tooltips_state = (state) ? TRUE : FALSE;
}


/*
 *	Changes the button layout.
 *
 *	The button must be one created by GUIButtonPixmap*()
 *	or GUIToggleButtonPixmap*().
 */
void GUIButtonChangeLayout(
	GtkWidget *w,
	const gboolean show_pixmap,
	const gboolean show_label
)
{
	GUIButtonData *d;

	if(w == NULL)
		return;

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return;

	w = d->label;
	if(w != NULL)
	{
		if(show_label)
			gtk_widget_show(w);
		else
			gtk_widget_hide(w);
	}

	/* Show/hide the icon GtkPixmap's toplevel boxt because it may
	 * be holding multiple icons that need to be shown/hidden
	 */
	w = d->icons_toplevel;
	if(w != NULL)
	{
		if(show_pixmap)
			gtk_widget_show(w); 
		else
			gtk_widget_hide(w);
	}

	w = d->arrow;
	if(w != NULL)
	{
		if(show_pixmap)
			gtk_widget_show(w);
		else
			gtk_widget_hide(w);
	}
}

/*
 *	Underlines the character in a button's label.
 */
void GUIButtonLabelUnderline(
	GtkWidget *w,
	const guint c
)
{
	GUIButtonData *d;

	if((w == NULL) || (c == '\0'))
		return;

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return;

	if((d->label != NULL) && (d->label_text != NULL))
	{
		GtkWidget *w = d->label;
		const gchar *text = d->label_text;
		gchar *s, *pattern = STRDUP(text);
		gint i = (gint)toupper((gint)c);

		for(s = pattern; *s != '\0'; s++)
		{
			if((gint)toupper((gint)*s) == i)
			{
				*s = '_';
				i = '\0';
			}
			else
			{
				*s = ' ';
			}
		}

		gtk_label_set_pattern(GTK_LABEL(w), pattern);

		g_free(pattern);
	}
}

/*
 *	Changes the button's icon and/or label.
 *
 *	If any value is NULL then it will not be modified.
 *
 *	The button must be one returned from GUIButtonPixmap*()
 *	or GUIToggleButtonPixmap*().
 */
void GUIButtonPixmapUpdate(
	GtkWidget *w,
	guint8 **icon,
	const gchar *label
)
{
	GtkWidget *parent;
	GUIButtonData *d;

	if(w == NULL)
		return;

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return;

	parent = d->main_box;
	if(parent == NULL)
		return;

	/* Update the icon? */
	if((icon != NULL) &&
	   ((const guint8 **)icon != d->icon_data)
	)
	{
		GdkBitmap *mask;
		GdkPixmap *pixmap = GUIPixmapNewFromXPMData(
			NULL,
			&mask,
			NULL,
			icon
		);
		if(pixmap != NULL)
		{
			gint width, height;
			gdk_window_get_size(pixmap, &width, &height);

			/* Record the new icon data pointer */
			d->icon_data = (const guint8 **)icon;

			/* Set the new icon pixmap */
			w = d->pixmap;
			if(w != NULL)
			{
				gtk_pixmap_set(GTK_PIXMAP(w), pixmap, mask);
			}
			else
			{
				d->pixmap = w = gtk_pixmap_new(pixmap, mask);
				gtk_box_pack_start(GTK_BOX(parent), w, TRUE, FALSE, 0);
				gtk_widget_show(w);
			}
			if(w != NULL)
			 {
				gtk_widget_set_usize(
					w,
					width + (2 * 0),
					height + (2 * 0)
				);
			}

			GDK_PIXMAP_UNREF(pixmap);
			GDK_BITMAP_UNREF(mask);
		}
	}

	/* Update the label text? */
	if(label != NULL)
	{
		w = d->label;
		if(w != NULL)
		{
			/* Change in label text? */
			if((d->label_text != NULL) ?
				strcmp(d->label_text, label) : TRUE
			)
			{
				/* Set new label text */
				g_free(d->label_text);
				d->label_text = STRDUP(label);
				gtk_label_set_text(GTK_LABEL(w), d->label_text);
			}
		}
	}
}

/*
 *	Changes the button's arrow and/or label.
 */
void GUIButtonArrowUpdate(
	GtkWidget *w,
	GtkArrowType arrow_type,
	const gint arrow_width,
	const gint arrow_height,
	const gchar *label
)
{
	GtkWidget *parent;
	GUIButtonData *d;

	if(w == NULL)
		return;

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return;

	parent = d->main_box;
	if(parent == NULL)
		return;

	/* Update arrow? */
	if(arrow_type >= 0)
	{
		w = d->arrow;
		if(w != NULL)
		{
			if(d->arrow_type != arrow_type)
			{
				d->arrow_type = arrow_type;
				gtk_arrow_set(
					GTK_ARROW(w),
					d->arrow_type,
					GTK_SHADOW_OUT
				);
			}
			gtk_widget_set_usize(w, arrow_width, arrow_height);
		}
	}

	/* Update label text? */
	if(label != NULL)
	{
		w = d->label;
		if(w != NULL)
		{
			/* Change in label text? */
			if((d->label_text != NULL) ?
				strcmp((const char *)d->label_text, (const char *)label) : TRUE
			)
			{
				/* Set new label text */
				g_free(d->label_text);
				d->label_text = STRDUP(label);
				gtk_label_set_text(GTK_LABEL(w), d->label_text);
			}
		}
	}
}

/*
 *	Gets the button's main GtkBox.
 */
GtkWidget *GUIButtonGetMainBox(GtkWidget *w)
{
	GUIButtonData *d;

	if(w == NULL)
		return(NULL);

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return(NULL);

	return(d->main_box);
}

/*
 *	Gets the button's GtkLabel.
 */
GtkWidget *GUIButtonGetLabel(GtkWidget *w)
{
	GUIButtonData *d;

	if(w == NULL)
		return(NULL);

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return(NULL);

	return(d->label);
}

/*
 *	Gets the button's GtkPixmap.
 */
GtkWidget *GUIButtonGetPixmap(GtkWidget *w)
{
	GUIButtonData *d;

	if(w == NULL)
		return(NULL);

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return(NULL);

	return(d->pixmap);
}

/*
 *	Gets the button's GtkArrow.
 */
GtkWidget *GUIButtonGetArrow(GtkWidget *w)
{
	GUIButtonData *d;

	if(w == NULL)
		return(NULL);

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return(NULL);

	return(d->arrow);
}

/*
 *	Gets the button's label text.
 */
const gchar *GUIButtonGetLabelText(GtkWidget *w)
{
	GUIButtonData *d;

	if(w == NULL)
		return(NULL);

	d = GUI_BUTTON_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_BUTTON_DATA_KEY
	));
	if(d == NULL)
		return(NULL);

	return(d->label_text);
}

/*
 *	GtkButton creation nexus.
 */
static GtkWidget *GUIButtonPixmapLabelHV(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn,
	gboolean horizontal
)
{
	const gint border = (label != NULL) ? 1 : 0;
	GtkWidget	*w,
					*parent;
	GUIButtonData *d;

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

	/* Create the button data */
	d = GUI_BUTTON_DATA(g_malloc0(
		sizeof(GUIButtonData)
	));
	if(d == NULL)
		return(NULL);

	/* GtkButton */
	d->button = w = gtk_button_new();
	gtk_object_set_data_full(
		GTK_OBJECT(w), GUI_BUTTON_DATA_KEY,
		d, GUIButtonDestroyCB
	);
	parent = w;

	/* Main GtkBox */
	if(horizontal)
		w = gtk_hbox_new(FALSE, 0);
	else
		w = gtk_vbox_new(FALSE, 0);
	d->main_box = w;
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;

	/* Icon */
	if(icon != NULL)
	{
		GdkBitmap *mask;
		GdkPixmap *pixmap = GUIPixmapNewFromXPMData(
			NULL,
			&mask,
			NULL,
			icon
		);
		if(pixmap != NULL)
		{
			GtkWidget *parent2;

			/* Record the icon data pointer */
			d->icon_data = (const guint8 **)icon;

			d->icons_toplevel = w = gtk_hbox_new(TRUE, 0);
			gtk_box_pack_start(GTK_BOX(parent), w, TRUE, FALSE, border);
			gtk_widget_show(w);
			parent2 = w;

			/* Icon GtkBox for client parenting of additional
			 * adjacent icons
			 */
			w = gtk_hbox_new(FALSE, 0);
			gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, FALSE, 0);
			gtk_widget_show(w);
			parent2 = w;

			/* Icon GtkPixmap */
			d->pixmap = w = gtk_pixmap_new(pixmap, mask);
			gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
			gtk_widget_show(w);

			GDK_PIXMAP_UNREF(pixmap);
			GDK_BITMAP_UNREF(mask);
		}
	}

	/* Label */
	if(label != NULL)
	{
		/* Copy/record the label text */
		d->label_text = STRDUP(label);

		/* GtkLabel */
		d->label = w = gtk_label_new(d->label_text);
		if(label_rtn != NULL)
			*label_rtn = w;
		gtk_label_set_justify(
			GTK_LABEL(w),
			horizontal ? GTK_JUSTIFY_LEFT : GTK_JUSTIFY_CENTER
		);
		gtk_box_pack_start(GTK_BOX(parent), w, TRUE, FALSE, border);
		gtk_widget_show(w);
	}

	return(d->button);
}

/*
 *	Creates a horizonal GtkButton with the specified icon and
 *	label.
 */
GtkWidget *GUIButtonPixmapLabelH(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn
)
{
	return(GUIButtonPixmapLabelHV(icon, label, label_rtn, 1));
}

/*
 *	Creates a vertical GtkButton with the specified icon and
 *	label.
 */
GtkWidget *GUIButtonPixmapLabelV(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn
)
{
	return(GUIButtonPixmapLabelHV(icon, label, label_rtn, 0));
}

/*
 *	Creates a GtkButton with the specified icon.
 */
GtkWidget *GUIButtonPixmap(guint8 **icon)
{
	return(GUIButtonPixmapLabelH(icon, NULL, NULL));
}


/*
 *	GtkButton with Arrow creation nexus.
 */
static GtkWidget *GUIButtonArrowLabelHV(
	const GtkArrowType arrow_type,
	const gint arrow_width, const gint arrow_height,
	const gchar *label,
	GtkWidget **label_rtn,
	const gboolean horizontal
)
{
	const gint border = (label != NULL) ? 1 : 0;
	GtkWidget *w, *parent;
	GUIButtonData *d;

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

	/* Create the button data */
	d = GUI_BUTTON_DATA(g_malloc0(
		sizeof(GUIButtonData)
	));
	if(d == NULL)
		return(NULL);

	/* GtkButton */
	d->button = w = gtk_button_new();
	gtk_object_set_data_full(
		GTK_OBJECT(w), GUI_BUTTON_DATA_KEY,
		d, GUIButtonDestroyCB
	);
	/* GtkArrows in GtkButtons need to have their GtkStateType
	 * manually reset in some cases, GUIButtonStateChangedCB()
	 * will manually reset the GtkArrow's GtkStateType
	 */
	gtk_signal_connect(
		GTK_OBJECT(w), "state_changed",
		GTK_SIGNAL_FUNC(GUIButtonStateChangedCB), d
	);
	parent = w;

	/* Main GtkBox */
	if(horizontal)
		w = gtk_hbox_new(FALSE, 0);
	else
		w = gtk_vbox_new(FALSE, 0);
	d->main_box = w;
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;

	/* GtkArrow */
	d->arrow_type = arrow_type;
	d->arrow = w = gtk_arrow_new(
		d->arrow_type,
		GTK_SHADOW_OUT
	);
	gtk_widget_set_usize(
		w,
		(arrow_width > 0) ? arrow_width : 20,
		(arrow_height > 0) ? arrow_height : 20
	);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, FALSE, border);
	gtk_widget_show(w);

	/* GtkLabel */
	if(label != NULL)
	{
		d->label_text = STRDUP(label);
		d->label = w = gtk_label_new(d->label_text);
		if(label_rtn != NULL)
			*label_rtn = w;
		gtk_label_set_justify(
			GTK_LABEL(w),
			horizontal ? GTK_JUSTIFY_LEFT : GTK_JUSTIFY_CENTER
		);
		gtk_box_pack_start(GTK_BOX(parent), w, TRUE, FALSE, border);
		gtk_widget_show(w);
	}

	return(d->button);
}

/*
 *	Creates a horizontal GtkButton with the specified arrow and
 *	label.
 */
GtkWidget *GUIButtonArrowLabelH(
	const GtkArrowType arrow_type,
	const gint arrow_width, const gint arrow_height,
	const gchar *label,
	GtkWidget **label_rtn
)
{
	return(GUIButtonArrowLabelHV(
		arrow_type, arrow_width, arrow_height,
		label, label_rtn, TRUE
	));
}

/*
 *	Creates a vertical GtkButton with the specified arrow and
 *	label.
 */
GtkWidget *GUIButtonArrowLabelV(
	const GtkArrowType arrow_type,
	const gint arrow_width, const gint arrow_height,
	const gchar *label,
	GtkWidget **label_rtn
)
{
	return(GUIButtonArrowLabelHV(
		arrow_type, arrow_width, arrow_height,
		label, label_rtn, FALSE
	));
}

/*
 *	Creates a GtkButton with the specified arrow.
 */
GtkWidget *GUIButtonArrow(
	const GtkArrowType arrow_type,
	const gint arrow_width, const gint arrow_height
)
{
	return(GUIButtonArrowLabelHV(
		arrow_type, arrow_width, arrow_height,
		NULL, NULL, FALSE
	));
}


/*
 *	GtkToggleButton creation nexus.
 */
GtkWidget *GUIToggleButtonPixmapLabelHV(
	guint8 **icon,
	const gchar *label,
	GtkWidget **label_rtn,
	const gboolean horizontal
)
{
	const gint border = (label != NULL) ? 1 : 0;
	GtkWidget	*w,
					*parent;
	GUIButtonData *d;

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

	/* Create the button data */
	d = GUI_BUTTON_DATA(g_malloc0(
		sizeof(GUIButtonData)
	));
	if(d == NULL)
		return(NULL);

	/* GtkToggleButton */
	d->button = w = gtk_toggle_button_new();
	gtk_object_set_data_full(
		GTK_OBJECT(w), GUI_BUTTON_DATA_KEY,
		d, GUIButtonDestroyCB
	);
	parent = w;

	/* Main GtkBox */
	if(horizontal)
		w = gtk_hbox_new(FALSE, 0);
	else
		w = gtk_vbox_new(FALSE, 0);
	d->main_box = w;
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;

	/* Icon */
	if(icon != NULL)
	{
		GdkBitmap *mask;
		GdkPixmap *pixmap = GUIPixmapNewFromXPMData(
			NULL,
			&mask,
			NULL,
			icon
		);
		if(pixmap != NULL)
		{
			GtkWidget *parent2;

			/* Record the icon data */
			d->icon_data = (const guint8 **)icon;

			d->icons_toplevel = w = gtk_hbox_new(TRUE, 0);
			gtk_box_pack_start(GTK_BOX(parent), w, TRUE, FALSE, border);
			gtk_widget_show(w);
			parent2 = w;

			/* Icon GtkBox for client parenting of additional
			 * adjacent icons
			 */
			w = gtk_hbox_new(FALSE, 0);
			gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, FALSE, 0);
			gtk_widget_show(w);
			parent2 = w;

			/* Icon GtkPixmap */
			d->pixmap = w = gtk_pixmap_new(pixmap, mask);
			gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
			gtk_widget_show(w);

			GDK_PIXMAP_UNREF(pixmap);
			GDK_BITMAP_UNREF(mask);
		}
	}

	/* Label */
	if(label != NULL)
	{
		/* Copy/record the label text */
		d->label_text = STRDUP(label);

		/* GtkLabel */
		d->label = w = gtk_label_new(d->label_text);
		if(label_rtn != NULL)
			*label_rtn = w;
		gtk_label_set_justify(
			GTK_LABEL(w),
			horizontal ? GTK_JUSTIFY_LEFT : GTK_JUSTIFY_CENTER
		);
		gtk_box_pack_start(GTK_BOX(parent), w, TRUE, FALSE, border);
		gtk_widget_show(w);
	}

	return(d->button);
}

/*
 *	Creates a horizonal GtkToggleButton with the specified icon and
 *	label.
 */
GtkWidget *GUIToggleButtonPixmapLabelH(
	guint8 **icon, const gchar *label, GtkWidget **label_rtn
)
{
	return(GUIToggleButtonPixmapLabelHV(icon, label, label_rtn, 1));
}

/*
 *	Creates a vertical GtkToggleButton with the specified icon and
 *	label.
 */
GtkWidget *GUIToggleButtonPixmapLabelV(
	guint8 **icon, const gchar *label, GtkWidget **label_rtn
)
{
	return(GUIToggleButtonPixmapLabelHV(icon, label, label_rtn, 0));
}

/*
 *	Creates a GtkToggleButton with the specified icon.
 */
GtkWidget *GUIToggleButtonPixmap(guint8 **icon)
{
	return(GUIToggleButtonPixmapLabelHV(icon, NULL, NULL, 0)); 
}


/*
 *	Prompt creation nexus
 */
static GtkWidget *GUIPromptBarOrBrowse(
	guint8 **icon, const gchar *label,
	gpointer *label_rtn, gpointer *entry_rtn, gpointer *browse_rtn
)
{
	const gint border_minor = 2;
	gint		attach_x, attach_y;
	GtkWidget	*w,
					*parent;

	/* Reset returns */
	if(label_rtn != NULL)
		*label_rtn = NULL;
	if(entry_rtn != NULL)
		*entry_rtn = NULL;
	if(browse_rtn != NULL)
		*browse_rtn = NULL;

	/* Create prompt bar parent */
	parent = w = gtk_table_new(
		1,
		((icon != NULL) ? 1 : 0) +
		((label != NULL) ? 1 : 0) +
		1 +
		((browse_rtn != NULL) ? 1 : 0),
		FALSE
	);
	gtk_table_set_col_spacings(GTK_TABLE(w), border_minor);
	attach_x = 0;
	attach_y = 0;

	/* Create icon */
	if(icon != NULL)
	{
		GdkBitmap *mask;
		GdkPixmap *pixmap = GUIPixmapNewFromXPMData(
			NULL,
			&mask,
			NULL,
			icon
		);
		if(pixmap != NULL)
		{
			gint width, height;

			gdk_window_get_size(pixmap, &width, &height);

			attach_x = 0;
			w = gtk_pixmap_new(pixmap, mask);
			gtk_widget_set_usize(w, width, height);
			gtk_table_attach(
				GTK_TABLE(parent), w,
				(guint)attach_x, (guint)(attach_x + 1),
				(guint)attach_y, (guint)(attach_y + 1),
				0,
				0,
				0, 0
			);
			gtk_widget_show(w);

			GDK_PIXMAP_UNREF(pixmap);
			GDK_BITMAP_UNREF(mask);
		}
	}

	/* Create label? */
	if(label != NULL)
	{
		attach_x = (icon != NULL) ? 1 : 0;

		w = gtk_label_new(label);
		if(label_rtn != NULL)
			*label_rtn = w;
		gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT);
		gtk_table_attach(
			GTK_TABLE(parent), w,
			(guint)attach_x, (guint)(attach_x + 1),
			(guint)attach_y, (guint)(attach_y + 1),
			0,
			0,
			0, 0
		);
		gtk_widget_show(w);
	}

	/* Create text entry? */
	if(TRUE)
	{
		attach_x = ((icon != NULL) ? 1 : 0) +
			((label != NULL) ? 1 : 0);

		w = gtk_entry_new();
		if(entry_rtn != NULL)
			*entry_rtn = w;
		gtk_table_attach(
			GTK_TABLE(parent), w,
			(guint)attach_x, (guint)(attach_x + 1),
			(guint)attach_y, (guint)(attach_y + 1),
			GTK_SHRINK | GTK_EXPAND | GTK_FILL,
			0,
			0, 0
		);
		gtk_widget_show(w);
	}

	/* Create browse button? */
	if(browse_rtn != NULL)
	{
		attach_x = ((icon != NULL) ? 1 : 0) +
			((label != NULL) ? 1 : 0) +
			1;

		w = GUIButtonPixmap(
			(guint8 **)icon_folder_opened_20x20_xpm
		);
		*browse_rtn = w;
		gtk_table_attach(
			GTK_TABLE(parent), w,
			(guint)attach_x, (guint)(attach_x + 1),
			(guint)attach_y, (guint)(attach_y + 1),
			0,
			0,
			0, 0
		);
		gtk_widget_show(w);
	}

	return(parent);
}

/*
 *	Creates a new prompt with an icon, label, entry, and browse
 *	button.
 */
GtkWidget *GUIPromptBarWithBrowse(
	guint8 **icon, const gchar *label,
	gpointer *label_rtn, gpointer *entry_rtn, gpointer *browse_rtn,
	gpointer browse_data, void (*browse_cb)(GtkWidget *, gpointer)
)
{
	GtkWidget *parent;

	parent = GUIPromptBarOrBrowse(
		icon, label, label_rtn, entry_rtn, browse_rtn
	);

	/* Set signal callback for the browse button */
	if((browse_rtn != NULL) && (browse_cb != NULL))
	{
		GtkWidget *w = (GtkWidget *)*browse_rtn;
		if(w != NULL)
			gtk_signal_connect(
				GTK_OBJECT(w), "clicked",
				GTK_SIGNAL_FUNC(browse_cb), browse_data
			);
	}

	return(parent);
}

/*
 *	Creates a new prompt with an icon, label, and entry.
 */
GtkWidget *GUIPromptBar(
	guint8 **icon, const gchar *label,
	gpointer *label_rtn, gpointer *entry_rtn
)
{
	return(GUIPromptBarOrBrowse(
		icon, label, label_rtn, entry_rtn, NULL
	));
}


/*
 *	Creates a new GtkTargetEntry.
 */
GtkTargetEntry *gtk_target_entry_new(void)
{
	return((GtkTargetEntry *)g_malloc0(sizeof(GtkTargetEntry)));
}

/*
 *	COppies the GtkTargetEntry.
 */
GtkTargetEntry *gtk_target_entry_copy(const GtkTargetEntry *entry)
{
	const GtkTargetEntry *src;
	GtkTargetEntry *tar;

	if(entry == NULL)
		 return(NULL);

	src = entry;
	tar = gtk_target_entry_new();
	if(tar == NULL)
		return(NULL);

	tar->target = STRDUP(src->target);
	tar->flags = src->flags;
	tar->info = src->info;

	return(tar);
}

/*
 *	Deletes the GtkTargetEntry.
 */
void gtk_target_entry_delete(GtkTargetEntry *entry)
{
	if(entry == NULL)
		 return;

	g_free(entry->target);
	g_free(entry);
}


/*
 *	Sets the DDE data.
 *
 *	The w specifies the owner GtkWidget that will be the owner
 *	of the data.
 *
 *	The selection_atom specifies the selection GdkAtom, if this
 *	value is GDK_NONE then GDK_SELECTION_PRIMARY will be used
 *	instead.
 *
 *	The t specifies the time stamp, this value can also be
 *	GDK_CURRENT_TIME.
 *
 *	The target_atom specifies the data type GdkAtom, this value
 *	may not be GDK_NONE.
 *
 *	The data and length specifies the data which will be transfered
 *	to the owner GtkWidget. The data and length should not be
 *	referenced again after this call.
 */
void GUIDDESetDirect(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	GdkAtom target_atom,
	guint8 *data, const gint length
)
{
	GList *glist;
	GdkAtom type_atom = gdk_atom_intern(GUI_TYPE_NAME_STRING, FALSE);
	GUISelectionData *sel_data;
	GUISelectionOwner *owner;

	if(selection_atom == GDK_NONE)
		selection_atom = GDK_SELECTION_PRIMARY;

	if((w == NULL) || (target_atom == GDK_NONE) ||
	   (data == NULL) || (length <= 0)
	)
	{
		g_free(data);
		return;
	}

	if(GTK_WIDGET_NO_WINDOW(w))
	{
		g_printerr(
"GUIDDESetDirect(): Failed:\
 GtkWidget %p does not have a GdkWindow.\n",
			w
		);
		g_free(data);
		return;
	}

	/* Get the owner data from the GtkWidget */
	owner = GUI_SELECTION_OWNER(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_SELECTION_OWNER_KEY
	));
	if(owner == NULL)
	{
		/* The owner data was not set, create it */
		owner = GUI_SELECTION_OWNER(g_malloc0(sizeof(GUISelectionOwner)));
		if(owner == NULL)
		{
			g_free(data);
			return;
		}

		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_SELECTION_OWNER_KEY,
			owner, GUIDDEOwnerDestroyCB
		);
		owner->w = w;
		owner->selection_clear_event_sigid = gtk_signal_connect(
			GTK_OBJECT(w), "selection_clear_event",
			GTK_SIGNAL_FUNC(GUIDDESelectionClearEventCB), owner
		);
		owner->selection_get_sigid = gtk_signal_connect(
			GTK_OBJECT(w), "selection_get",
			GTK_SIGNAL_FUNC(GUIDDESelectionGetCB), owner
		);
	}

	/* Get the selection data based on the specified selection */
	sel_data = NULL;
	for(glist = owner->datas_list;
		glist != NULL;
		glist = g_list_next(glist)
	)
	{
		sel_data = GUI_SELECTION_DATA(glist->data);
		if(sel_data == NULL)
			continue;

		if(sel_data->selection_atom == selection_atom)
			break;
	}
	if(glist == NULL)
	{
		sel_data = GUI_SELECTION_DATA(g_malloc0(sizeof(GUISelectionData)));
		if(sel_data == NULL)
		{
			g_free(data);
			return;
		}

		sel_data->selection_atom = selection_atom;

		owner->datas_list = g_list_append(
			owner->datas_list,
			sel_data
		);
	}
	if(sel_data == NULL)
	{
		g_free(data);
		return;
	}

	sel_data->target_atom = target_atom;
	sel_data->type_atom = type_atom;

	/* Delete the owner's existing data and transfer the new data */
	g_free(sel_data->data);
	sel_data->data = data;
	sel_data->length = length;

	/* Set the widget as supporting the selection of the
	 * specified target
	 */
	gtk_selection_add_target(
		w,					/* Widget */
		selection_atom,			/* Selection */
		target_atom,			/* Target */
		0					/* Info */
	);

	/* Set the selection owner */
	if(!gtk_selection_owner_set(
		w,					/* Owner GtkWidget */
		selection_atom,			/* Selection */
		(guint32)t				/* Time */
	))
	{
		/* Unable to set the selection owner */
		sel_data->target_atom = GDK_NONE;
		sel_data->type_atom = GDK_NONE;
		g_free(sel_data->data);
		sel_data->data = NULL;
		sel_data->length = 0;
		return;
	}
}

/*
 *	Sets the DDE data.
 *
 *	The w specifies the owner GtkWidget that will be the owner
 *	of the data.
 *
 *	The selection_atom specifies the selection GdkAtom, if this
 *	value is GDK_NONE then GDK_SELECTION_PRIMARY will be used
 *	instead.
 *
 *	The t specifies the time stamp, this value can also be
 *	GDK_CURRENT_TIME.
 *
 *	The target_atom specifies the data type GdkAtom, this value
 *	may not be GDK_NONE.
 *
 *	The data and length specifies the data which will be coppied
 *	to the owner GtkWidget.
 */
void GUIDDESet(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	GdkAtom target_atom,
	const guint8 *data, const gint length
)
{
	GUIDDESetDirect(
		w,
		selection_atom,
		t,
		target_atom,
		g_memdup(data, length), length
	);
}

/*
 *	Gets the DDE data.
 *
 *	The w specifies the GtkWidget making the request.
 *
 *	The selection_atom specifies the selection GdkAtom, if this
 *	value is GDK_NONE then GDK_SELECTION_PRIMARY will be used
 *	instead.
 *
 *	The t specifies the time stamp, this value can also be
 *	GDK_CURRENT_TIME.
 *
 *	The target_atom specifies the data type GdkAtom, this value
 *	may not be GDK_NONE.
 *
 *	The length specifies the length of the returned data in bytes.
 *
 *	The calling function must delete the returned data.
 */
guint8 *GUIDDEGet(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	GdkAtom target_atom,
	gint *length
)
{
	guint8 *data;
	guint selection_received_sigid;
	GUISelectionData *sel_data;

	if(length != NULL)
		*length = 0;

	if(selection_atom == GDK_NONE)
		selection_atom = GDK_SELECTION_PRIMARY;

	if((w == NULL) || (target_atom == GDK_NONE))
		return(NULL);

	if(GTK_WIDGET_NO_WINDOW(w))
	{
		g_printerr(
"GUIDDEGet(): Failed:\
 GtkWidget %p does not have a GdkWindow.\n",
			w
		);
		return(NULL);
	}

	/* Create the selection data return */
	sel_data = GUI_SELECTION_DATA(g_malloc0(sizeof(GUISelectionData)));
	if(sel_data == NULL)
		return(NULL);

	sel_data->selection_atom = selection_atom;
	sel_data->target_atom = target_atom;
	sel_data->type_atom = GDK_NONE;
	sel_data->data = NULL;
	sel_data->length = -1;			/* -1 means data not received */

	/* Set up the requesting GtkWidget to receive the
	 * "selection_received" signal
	 */
	selection_received_sigid = gtk_signal_connect(
		GTK_OBJECT(w), "selection_received",
		GTK_SIGNAL_FUNC(GUIDDESelectionReceivedCB), sel_data
	);

	/* Request the selection data */
	if(!gtk_selection_convert(
		w,					/* Widget */
		selection_atom,			/* Selection */
		target_atom,			/* Target */
		(guint32)t				/* Time */
	))
	{
		/* Unable to get the selection data */
		gtk_signal_disconnect(
			GTK_OBJECT(w),
			selection_received_sigid
		);
		GUISelectionDataDelete(sel_data);
		return(NULL);
	}

	/* Wait for the selection data to arrive
	 *
	 * This is only needed if the owner is on another application,
	 * otherwise gtk_selection_convert() calls the callback
	 * immediately
	 */
	while(sel_data->length < 0)
		gtk_events_process();

	/* Disconnect the "selection_received" signal */
	gtk_signal_disconnect(
		GTK_OBJECT(w),
		selection_received_sigid
	);

	/* Got the selection, transfer the selection return's data to
	 * the return data
	 */
	data = (guint8 *)sel_data->data;
	if(length != NULL)
		*length = sel_data->length;
	sel_data->data = NULL;
	sel_data->length = 0;

	/* Delete the selection return data */
	GUISelectionDataDelete(sel_data);

	return(data);
}

/*
 *	Clears the DDE from the widget.
 *
 *	The w specifies the GtkWidget who's DDE data is to be cleared.
 */
void GUIDDEClear(
	GtkWidget *w,
	GdkAtom selection_atom
)
{
	GUISelectionOwner *owner;

	if(w == NULL)
		return;

	/* Get the owner data from the GtkWidget */
	owner = GUI_SELECTION_OWNER(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_SELECTION_OWNER_KEY
	));
	if(owner == NULL)
		return;

	/* Clear the selection data */
	if(owner->datas_list != NULL)
	{
		GList *glist;
		GUISelectionData *sel_data;
		for(glist = owner->datas_list;
			glist != NULL;
			glist = g_list_next(glist)
		)
		{
			sel_data = GUI_SELECTION_DATA(glist->data);
			if(sel_data == NULL)
				continue;

			/* Is this the selection data to clear? */
			if(sel_data->selection_atom == selection_atom)
			{
				sel_data->target_atom = GDK_NONE;
				sel_data->type_atom = GDK_NONE;
				g_free(sel_data->data);
				sel_data->data = NULL;
				sel_data->length = 0;
			}
		}
	}

	/* Delete the GtkSelectionData on the owner GtkWidget */
	if(owner->w != NULL)
		gtk_selection_remove_all(owner->w);
}


/*
 *	Sets the DDE as binary data.
 *
 *	The w specifies the owner GtkWidget that will be the owner
 *	of the data.
 *
 *	The selection_atom specifies the selection GdkAtom, if this
 *	value is GDK_NONE then GDK_SELECTION_PRIMARY will be used
 *	instead.
 *
 *	The t specifies the time stamp, this value can also be
 *	GDK_CURRENT_TIME.
 *
 *	The target_atom specifies the data type GdkAtom, this value
 *	may not be GDK_NONE.
 *
 *	The data and length specifies the binary data which will be
 *	coppied to the owner GtkWidget.
 */
void GUIDDESetBinary(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	const guint8 *data, const gint length
)
{
	GUIDDESet(
		w,
		selection_atom,
		t,
		gdk_atom_intern(GUI_TARGET_NAME_STRING, FALSE),
		data, length
	);
}

/*
 *	Gets the DDE binary data.
 *
 *	The w specifies the GtkWidget making the request.
 *
 *	The selection_atom specifies the selection GdkAtom, if this
 *	value is GDK_NONE then GDK_SELECTION_PRIMARY will be used
 *	instead.
 *
 *	The t specifies the time stamp, this value can also be
 *	GDK_CURRENT_TIME.
 *
 *	The length specifies the length of the returned data in bytes.
 *
 *	The calling function must delete the returned data.
 */
guint8 *GUIDDEGetBinary(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	gint *length
)
{
	return(GUIDDEGet(
		w,
		selection_atom,
		t,
		gdk_atom_intern(GUI_TARGET_NAME_STRING, FALSE),
		length
	));
}


/*
 *	Sets the DDE as a string.
 *
 *	The w specifies the owner GtkWidget that will be the owner
 *	of the data.
 *
 *	The selection_atom specifies the selection GdkAtom, if this
 *	value is GDK_NONE then GDK_SELECTION_PRIMARY will be used
 *	instead.
 *
 *	The t specifies the time stamp, this value can also be
 *	GDK_CURRENT_TIME.
 *
 *	The target_atom specifies the data type GdkAtom, this value
 *	may not be GDK_NONE.
 *
 *	The data specifies the string which will be coppied to the
 *	owner GtkWidget.
 */
void GUIDDESetString(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t,
	const gchar *data
)
{
	GUIDDESet(
		w,
		selection_atom,
		t,
		gdk_atom_intern(GUI_TARGET_NAME_STRING, FALSE),
		(const guint8 *)data, STRLEN(data)
	);
}

/*
 *	Gets the DDE binary data.
 *
 *	The w specifies the GtkWidget making the request.
 *
 *	The selection_atom specifies the selection GdkAtom, if this
 *	value is GDK_NONE then GDK_SELECTION_PRIMARY will be used
 *	instead.
 *
 *	The t specifies the time stamp, this value can also be
 *	GDK_CURRENT_TIME.
 *
 *	The calling function must delete the returned data.
 */
gchar *GUIDDEGetString(
	GtkWidget *w,
	GdkAtom selection_atom,
	const gulong t
)
{
	gint length;
	gchar *s;
	guint8 *buf = GUIDDEGet(
		w,
		selection_atom,
		t,
		gdk_atom_intern(GUI_TARGET_NAME_STRING, FALSE),
		&length
	);
	if(buf == NULL)
		return(NULL);

	s = (gchar *)g_realloc(
		buf,
		(length + 1) * sizeof(gchar)
	);
	if(s != NULL)
		s[length] = '\0';

	return(s);
}


/*
 *	Sets up widget as a DND drag source.
 *
 *	The w specifies the GtkWidget. The GtkWidget must have a
 *	GdkWindow in order for DND to work on it.
 *
 *	The types_list and ntypes specifies the DND types
 *	list, which determines what type of drags the GtkWidget will
 *	provide.
 *
 *	The allowed_actions specifies the GdkDragActions that are accepted,
 *	which can be any of GDK_ACTION_*.
 *
 *	The buttons specifies the GdkModifierTypes that are accepted,
 *	which can be any of GDK_BUTTON*_MASK.
 *
 *	The drag_begin_cb specifies the "drag_begin" signal callback.
 *
 *	The drag_data_get_cb specifies the "drag_data_get" signal
 *	callback.
 *
 *	The drag_data_delete_cb specifies the "drag_data_delete"
 *	signal callback.
 *
 *	The drag_end_cb specifies the "drag_end" signal callback.
 *
 *	The data specifies the data for all the callbacks.
 */
void GUIDNDSetSrc(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction allowed_actions,
	const GdkModifierType buttons,
	void (*drag_begin_cb)(
			GtkWidget *, GdkDragContext *,
			gpointer
	),
	void (*drag_data_get_cb)(
			GtkWidget *, GdkDragContext *,
			GtkSelectionData *,
			guint, guint,
			gpointer
	),
	void (*drag_data_delete_cb)(
			GtkWidget *, GdkDragContext *,
			gpointer
	),
	void (*drag_end_cb)(
			GtkWidget *, GdkDragContext *,
			gpointer
	),
	gpointer data
)
{
	gint i;
	GUIDNDSourceData *d;

	if((w == NULL) || (types_list == NULL) || (ntypes <= 0))
		return;

	/* The GtkWidget must have a GdkWindow */
	if(GTK_WIDGET_NO_WINDOW(w))
	{
		g_printerr(
"GUIDNDSetSrc(): GtkWidget %p does not have a GdkWindow.\n",
			w
		);
		return;
	}

	/* Get the DND source data from the GtkWidget */
	d = GUI_DND_SOURCE_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_DND_SOURCE_DATA_KEY
	));
	if(d == NULL)
	{
		/* No DND source data was set on this GtkWidget so create
		 * and set it for the first time
		 */
		d = GUI_DND_SOURCE_DATA(g_malloc0(sizeof(GUIDNDSourceData)));
		if(d == NULL)
			return;

		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_DND_SOURCE_DATA_KEY,
			d, GUIDNDSourceDataDestroyCB
		);

		/* Add this GtkWidget to the DND source GtkWidgets list */
		gui_dnd_source_widgets_list = g_list_append(
			gui_dnd_source_widgets_list,
			w
		);
	}

	/* Set the DND source data */
	d->widget = w;
	if(d->targets_list != NULL)
	{
		g_list_foreach(
			d->targets_list,
			(GFunc)gtk_target_entry_delete,
			NULL
		);
		g_list_free(d->targets_list);
		d->targets_list = NULL;
	}
	for(i = 0; i < ntypes; i++)
		d->targets_list = g_list_append(
			d->targets_list,
			gtk_target_entry_copy(&types_list[i])
		);

	/* Set the widget as a DND drag source */
	gtk_drag_source_set(
		w,
		buttons,
		types_list, ntypes,
		allowed_actions
	);

	/* Set the "drag_begin" signal callback, our local callback must
	 * be connected before GTK internal drag begin callbacks
	 */
	gtk_signal_connect_after(
		GTK_OBJECT(w), "drag_begin",
		GTK_SIGNAL_FUNC(GUIDNDDragBeginCB), d
	);
	if(drag_begin_cb != NULL)
		gtk_signal_connect_after(
			GTK_OBJECT(w), "drag_begin",
			GTK_SIGNAL_FUNC(drag_begin_cb), data
		);

	/* Set the "drag_data_get" signal callback */
	if(drag_data_get_cb != NULL)
		gtk_signal_connect_after(
			GTK_OBJECT(w), "drag_data_get",
			GTK_SIGNAL_FUNC(drag_data_get_cb), data
		);

	/* Set the "drag_data_delete" signal callback */
	if(drag_data_delete_cb != NULL)
		gtk_signal_connect_after(
			GTK_OBJECT(w), "drag_data_delete",
			GTK_SIGNAL_FUNC(drag_data_delete_cb), data
		);

	/* Set the "drag_end" signal callback */
	if(drag_end_cb != NULL)
		gtk_signal_connect_after(
			GTK_OBJECT(w), "drag_end",
			GTK_SIGNAL_FUNC(drag_end_cb), data
		);

	/* Set our own "drag_end" signal callback to be called after
	 * the one for the client function
	 */
	gtk_signal_connect_after(
		GTK_OBJECT(w), "drag_end",
		GTK_SIGNAL_FUNC(GUIDNDDragEndCB), d
	);
}

/*
 *	Changes the widget's DND source types.
 *
 *	The w specifies the GtkWidget. The GtkWidget must have a
 *	GdkWindow in order for DND to work on it. A prior call to
 *	GUIDNDSetSrc() is required to set up the GtkWidget.
 *
 *	The types_list and ntypes specifies the DND types
 *	list, which determines what type of drags the GtkWidget will
 *	provide.
 *
 *	The allowed_actions specifies the GdkDragActions that are accepted,
 *	which can be any of GDK_ACTION_*.
 *
 *	The buttons specifies the GdkModifierTypes that are accepted,
 *	which can be any of GDK_BUTTON*_MASK.
 */
void GUIDNDSetSrcTypes(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction allowed_actions,
	const GdkModifierType buttons
)
{
	GUIDNDSourceData *d;

	if(w == NULL)
		return;

	/* Get the DND source data from the GtkWidget */
	d = GUI_DND_SOURCE_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_DND_SOURCE_DATA_KEY
	));
	if(d != NULL)
	{
		gint i;

		/* Set the DND source data */
		if(d->targets_list != NULL)
		{
			g_list_foreach(
				d->targets_list,
				(GFunc)gtk_target_entry_delete,
				NULL
			);
			g_list_free(d->targets_list);
			d->targets_list = NULL;
		}
		for(i = 0; i < ntypes; i++)
			d->targets_list = g_list_append(
				d->targets_list,
				gtk_target_entry_copy(&types_list[i])
			);
	}

	/* Change the GtkWidget's DND source types */
	gtk_drag_source_unset(w);
	if(types_list != NULL)
		gtk_drag_source_set(
			w,
			buttons,
			types_list, ntypes,
			allowed_actions
		);
}

/*
 *	Sets up a widget as a DND drop target.
 *
 *	The w specifies the GtkWidget. The GtkWidget must have a
 *	GdkWindow in order for DND to work on it.
 *
 *	The types_list and ntypes specifies the DND types
 *	list, which determines what type of drops the GtkWidget will
 *	accept.
 *
 *	The allowed_actions specifies the GdkDragActions that are accepted,
 *	which can be any of GDK_ACTION_*.
 *
 *	The default_action_same specifies the default GdkDragAction
 *	if the drag originates from the same GtkWidget.
 *
 *	The default_action specifies the default GdkDragAction if the
 *	drag originates a different GtkWidget.
 *
 *	The drag_data_recieved_cb and drag_data_received_data
 *	specifies the "drag_data_received" signal callback.
 *
 *	If highlight is TRUE then automatic highlighting of the
 *	GtkWidget will be performed when a drag comes over it.
 */
void GUIDNDSetTar(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction allowed_actions,
	const GdkDragAction default_action_same,
	const GdkDragAction default_action,
	void (*drag_data_recieved_cb)(
			GtkWidget *, GdkDragContext *,
			gint, gint,
			GtkSelectionData *,
			guint,
			guint,
			gpointer     
	),
	gpointer drag_data_received_data,
	const gboolean highlight
)
{
	gint i;
	GUIDNDTargetData *d;

	if((w == NULL) || (types_list == NULL) || (ntypes <= 0))
		return;

	/* The GtkWidget must have a GdkWindow */
	if(GTK_WIDGET_NO_WINDOW(w))
	{
		g_printerr(
"GUIDNDSetTar(): GtkWidget %p does not have GdkWindow.\n",
			w
		);
		return;
	}

	/* Get the DND target data from the GtkWidget */
	d = GUI_DND_TARGET_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_DND_TARGET_DATA_KEY
	));
	if(d == NULL)
	{
		/* No DND target data was set on this GtkWidget so create
		 * and set it for the first time
		 */
		d = GUI_DND_TARGET_DATA(g_malloc0(sizeof(GUIDNDTargetData)));
		if(d == NULL)
			return;

		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_DND_TARGET_DATA_KEY,
			d, GUIDNDTargetDataDestroyCB
		);
	}

	/* Set the DND target data */
	d->flags = ((highlight) ? GUI_DND_TARGET_HIGHLIGHT : 0);
	d->widget = w;
	if(d->targets_list != NULL)
	{
		g_list_foreach(
			d->targets_list,
			(GFunc)gtk_target_entry_delete,
			NULL
		);
		g_list_free(d->targets_list);
		d->targets_list = NULL;
	}
	for(i = 0; i < ntypes; i++)
		d->targets_list = g_list_append(
			d->targets_list,
			gtk_target_entry_copy(&types_list[i])
		);
	d->allowed_actions = allowed_actions;
	d->default_action_same = default_action_same;
	d->default_action = default_action;

	/* Set the widget as a DND drop target */
	gtk_drag_dest_set(
		w,
		GTK_DEST_DEFAULT_DROP,	/* Use the built-in drop handler */
		types_list, ntypes,
		allowed_actions
	);

	/* Connect our callback signal after so that the calling
	 * function can override the signals' affects by connecting
	 * their callbacks after ours
	 */
	if(d->drag_leave_sigid == 0)
		d->drag_leave_sigid = gtk_signal_connect_after(
			GTK_OBJECT(w), "drag_leave",
			GTK_SIGNAL_FUNC(GUIDNDTargetDragLeaveCB), d
		);
	if(d->drag_motion_sigid == 0)
		d->drag_motion_sigid = gtk_signal_connect_after(
			GTK_OBJECT(w), "drag_motion",
			GTK_SIGNAL_FUNC(GUIDNDTargetDragMotionCB), d
		);

	/* Connect the calling function's "drag_data_received"
	 * signal callback.
	 */
	if(drag_data_recieved_cb != NULL)
		gtk_signal_connect(
			GTK_OBJECT(w), "drag_data_received",
			GTK_SIGNAL_FUNC(drag_data_recieved_cb),
			drag_data_received_data
		);
}

/*
 *	Changes the widget's DND target types.
 *
 *	The w specifies the GtkWidget. The GtkWidget must have a
 *	GdkWindow in order for DND to work on it. A prior call to
 *	GUIDNDSetTar() is required to set up the GtkWidget.
 *
 *	The types_list and ntypes specifies the DND types
 *	list, which determines what type of drops the GtkWidget will
 *	accept.
 *
 *	The allowed_actions specifies the GdkDragActions that are accepted,
 *	which can be any of GDK_ACTION_*.
 *
 *	The default_action_same specifies the default GdkDragAction
 *	if the drag originates from the same GtkWidget.
 *
 *	The default_action specifies the default GdkDragAction if the
 *	drag originates a different GtkWidget.
 *
 *	If highlight is TRUE then automatic highlighting of the
 *	GtkWidget will be performed when a drag comes over it.
 */
void GUIDNDSetTarTypes(
	GtkWidget *w,
	const GtkTargetEntry *types_list, const gint ntypes,
	const GdkDragAction allowed_actions,
	const GdkDragAction default_action_same,
	const GdkDragAction default_action,
	const gboolean highlight
)
{
	GUIDNDTargetData *d;

	if(w == NULL)
		return;

	/* Get the DND target data from the GtkWidget */
	d = GUI_DND_TARGET_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_DND_TARGET_DATA_KEY
	));
	if(d != NULL)
	{
		gint i;

		/* Set the DND target data */
		d->flags = ((highlight) ? GUI_DND_TARGET_HIGHLIGHT : 0);
		if(d->targets_list != NULL)
		{
			g_list_foreach(
				d->targets_list,
				(GFunc)gtk_target_entry_delete,
				NULL
			);
			g_list_free(d->targets_list);
			d->targets_list = NULL;
		}
		for(i = 0; i < ntypes; i++)
			d->targets_list = g_list_append(
				d->targets_list,
				gtk_target_entry_copy(&types_list[i])
			);
		d->allowed_actions = allowed_actions;
		d->default_action_same = default_action_same;
		d->default_action = default_action;
	}

	/* Change the GtkWidget's DND target types */
	gtk_drag_dest_unset(w);
	if(types_list != NULL)
		gtk_drag_dest_set(
			w,
			GTK_DEST_DEFAULT_DROP,	/* Use the built-in drop handler */
			types_list, ntypes,
			allowed_actions
		);
}

/*
 *	Gets the DND target GtkWidget's drag actions.
 */
void GUIDNDGetTarActions(
	GtkWidget *w,
	GdkDragAction *allowed_actions_rtn,
	GdkDragAction *default_action_same_rtn,
	GdkDragAction *default_action_rtn
)
{
	GUIDNDTargetData *d;

	if(allowed_actions_rtn != NULL)
		*allowed_actions_rtn = 0;
	if(default_action_same_rtn != NULL)
		*default_action_same_rtn = 0;
	if(default_action_rtn != NULL)
		*default_action_rtn = 0;

	if(w == NULL)
		return;

	/* Get the DND target data from the GtkWidget */
	d = GUI_DND_TARGET_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_DND_TARGET_DATA_KEY
	));
	if(d == NULL)
		return;

	if(allowed_actions_rtn != NULL)
		*allowed_actions_rtn = d->allowed_actions;
	if(default_action_same_rtn != NULL)
		*default_action_same_rtn = d->default_action_same;
	if(default_action_rtn != NULL)
		*default_action_rtn = d->default_action;
}

/*
 *	Determines the allowed drag action from the requested actions.
 *
 *	The requested_actions specifies the requested actions.
 *
 *	The allowed_actions specifies all the actions that are allowed.
 *
 *	The default_action specifies the default action to allow when
 *	more than one of the requested actions are allowed. The
 *	default action must be in the allowed_actions list. If
 *	allowed_actions is 0 then default_action must also be 0.
 *
 *	Returns exactly one of the requested actions or 0 if none of
 *	the requested actions are allowed.
 */
GdkDragAction GUIDNDDetermineDragAction(
	GdkDragAction requested_actions,
	GdkDragAction allowed_actions,
	GdkDragAction default_action
)
{
	/* Exactly one action requested? */
	switch(requested_actions)
	{
	  case GDK_ACTION_DEFAULT:
	  case GDK_ACTION_COPY:
	  case GDK_ACTION_MOVE:
	  case GDK_ACTION_LINK:
	  case GDK_ACTION_PRIVATE:
	  case GDK_ACTION_ASK:
		/* Is this action allowed? */
		if(requested_actions & allowed_actions)
			return(requested_actions);
		break;
	}

	/* Multiple actions, check if the default action is one of them */
	if(requested_actions & default_action)
		return(default_action);
	else
		return(0);
}

/*
 *	Checks if the GtkWidget will accept the target.
 *
 *	The w specifies the target GtkWidget.
 *
 *	The name specifies the target's name.
 *
 *	The flags specifies the GtkTargetFlags.
 *
 *	Returns TRUE if the GtkWidget will accept the target.
 */
gboolean GUIDNDIsTargetAccepted(
	GtkWidget *w,
	const gchar *name,
	const GtkTargetFlags flags
)
{
	GList *glist;
	GtkTargetEntry *entry;
	GUIDNDTargetData *d;

	if((w == NULL) || STRISEMPTY(name))
		return(FALSE);

	/* Get the DND data from the GtkWidget */
	d = GUI_DND_TARGET_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_DND_TARGET_DATA_KEY
	));
	if(d == NULL)
		return(FALSE);

	/* Check if the specified target name is in the list of targets
	 * specified on the GtkWidget
	 */
	for(glist = d->targets_list;
		glist != NULL;
		glist = g_list_next(glist)
	)
	{
		entry = (GtkTargetEntry *)glist->data;
		if(entry == NULL)
			continue;

		if(entry->target == NULL)
			continue;

		/* Target names match? */
		if(!g_strcasecmp(name, entry->target))
		{
			/* Must be the same application? */
			if(entry->flags & GTK_TARGET_SAME_APP)
			{
				if(!(flags & GTK_TARGET_SAME_APP))
					continue;
			}

			/* Must be the same GtkWidget? */
			if(entry->flags & GTK_TARGET_SAME_WIDGET)
			{
				if(!(flags & GTK_TARGET_SAME_WIDGET))
					continue;
			}

			return(TRUE);
		}
	}

	return(FALSE);
}

/*
 *	Sets the DND drag icon from the selected rows on the GtkCList
 *	or the selected nodes on the GtkCTree.
 *
 *	The w specifies the GtkCTree or GtkCList.
 *
 *	The cells_list specifies a GList of GtkCellText *,
 *	GtkCellPixmap *, or GtkCellPixText * cells. Only the first 5
 *	cells will be used.
 *
 *	The nitems specifies the total number of items, which can be
 *	greater than the number of items in cells_list. If nitems
 *	is greater than the number of items in cells_list then the
 *	number of additional items will be stated by text on the
 *	drag icon. If nitems is negative then additional items will
 *	not be stated.
 *
 *	The layout_orientation specifies whether to layout the items
 *	horizontally or vertically.
 *
 *	The icon_text_orientation specifies to either display the
 *	icon on the left and the text on the right
 *	(GTK_ORIENTATION_HORIZONTAL) or the icon on top and the text
 *	on the bottom (GTK_ORIENTATION_VERTICAL).
 *
 *	The item_spacing specifies the spacing between each item.
 *
 *	If drop_shadow is TRUE then a shadow will be rendered
 *	into the DND drag icon.
 *
 *	The hot_x and hot_y specifies the hot spot.
 */
static void GUIDNDSetDragIconFromWidgetNexus(
	GtkWidget *w,
	GList *cells_list,
	const gint nitems,
	const GtkOrientation layout_orientation,
	const GtkOrientation icon_text_orientation,
	const gint item_spacing,
	const gboolean drop_shadow,
	const gint hot_x, const gint hot_y
)
{
	const gint	max_width = gdk_screen_width(),
					max_height = gdk_screen_height(),
					shadow_offset_x = GUI_DND_DRAG_ICON_DEF_SHADOW_OFFSET_X,
					shadow_offset_y = GUI_DND_DRAG_ICON_DEF_SHADOW_OFFSET_Y;
	gint		nitems_in_list,
					item_x, item_y,
					width, height,		/* Size of the entire DND
											 * drag icon pixmap and mask */
					item_highest,
					icon_width, icon_height,
					spacing = 0,		/* Spacing between the icon
											 * and the text */
					font_height, font_ascent;
	const gchar *text = NULL;
	GList *glist;
	GdkBitmap	*mask,			/* DND drag icon mask */
					*icon_mask = NULL;
	GdkPixmap	*pixmap,		/* DND drag icon pixmap */
					*icon_pixmap = NULL;
	GdkWindow *window = w->window;
	GdkFont *font;
	GdkGC *mask_gc;
	const GtkStateType state = GTK_WIDGET_STATE(w);
	GtkCellText *cell_text;
	GtkCellPixmap *cell_pixmap;
	GtkCellPixText *cell_pixtext;
	GtkStyle *style = gtk_widget_get_style(w);
	if(style == NULL)
		return;

	font = style->font;
	if(font != NULL)
	{
		font_ascent = font->ascent;
		font_height = MAX((font->ascent + font->descent), 0);
	}
	else
	{
		font_ascent = 0;
		font_height = 0;
	}

	/* Calculate the size of the entire DND drag icon */
	width = 0;
	height = 0;
	item_highest = 0;
	for(glist = cells_list, nitems_in_list = 0;
		glist != NULL;
		glist = g_list_next(glist), nitems_in_list++
	)
	{
		/* Get the information from this cell */
		switch(*(GtkCellType *)glist->data)
		{
		  case GTK_CELL_EMPTY:
		  case GTK_CELL_WIDGET:
			text = NULL;
			spacing = 0;
			icon_pixmap = NULL;
			icon_mask = NULL;
			break;
		  case GTK_CELL_TEXT:
			cell_text = (GtkCellText *)glist->data;
			text = cell_text->text;
			spacing = 0;
			icon_pixmap = NULL;
			icon_mask = NULL;
			break;
		  case GTK_CELL_PIXMAP:
			cell_pixmap = (GtkCellPixmap *)glist->data;
			text = NULL;
			spacing = 0;
			icon_pixmap = cell_pixmap->pixmap;
			icon_mask = cell_pixmap->mask;
			break;
		  case GTK_CELL_PIXTEXT:
			cell_pixtext = (GtkCellPixText *)glist->data;
			text = cell_pixtext->text;
			spacing = cell_pixtext->spacing;
			icon_pixmap = cell_pixtext->pixmap;
			icon_mask = cell_pixtext->mask;
			break;
		}
		if((icon_pixmap != NULL) && (text != NULL))
		{
			gint	item_width = 0, item_height = 0,
					swidth;

			/* Get the size of the icon and text */
		    gdk_window_get_size(icon_pixmap, &icon_width, &icon_height);
			swidth = gdk_string_measure(font, text);

			/* Calculate the size of the row and update the subtotal
			 * size of the DND drag icon
			 */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				item_width = icon_width + spacing + swidth + 3;
				item_height = MAX(icon_height, (font_height + 3));
				break;
			  case GTK_ORIENTATION_VERTICAL:
				item_width = MAX(icon_width, (swidth + 3));
				item_height = icon_height + spacing + font_height + 3;
				break;
			}
			switch(layout_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				width += item_width + item_spacing;
				if(height < item_height)
					height = item_height;
				break;
			  case GTK_ORIENTATION_VERTICAL:
				if(width < item_width)
					width = item_width;
				height += item_height + item_spacing;
				break;
			}
			if(item_height > item_highest)
				item_highest = item_height;
		}
		else if(icon_pixmap != NULL)
		{
			gint item_width = 0, item_height = 0;

			/* Get the size of the icon */
		    gdk_window_get_size(icon_pixmap, &icon_width, &icon_height);

			/* Calculate the size of the row and update the subtotal
			 * size of the DND drag icon
			 */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
			  case GTK_ORIENTATION_VERTICAL:
				item_width = icon_width;
				item_height = icon_height;
				break;
			}
			switch(layout_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				width += item_width + item_spacing;
				if(height < item_height)
					height = item_height;
				break;
			  case GTK_ORIENTATION_VERTICAL:
				if(width < item_width)
					width = item_width;
				height += item_height + item_spacing;
				break;
			}
			if(item_height > item_highest)
				item_highest = item_height;
		}
		else if(text != NULL)
		{
			gint	item_width = 0, item_height = 0,
					swidth;

			/* Get the width needed to draw the text */
			swidth = gdk_string_measure(font, text);

			/* Calculate the size of the row and update the subtotal
			 * size of the DND drag icon
			 */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
			  case GTK_ORIENTATION_VERTICAL:
				item_width = swidth + 3;
				item_height = font_height + 3;
				break;
			}
			switch(layout_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				width += item_width + item_spacing;
				if(height < item_height)
					height = item_height;
				break;
			  case GTK_ORIENTATION_VERTICAL:
				if(width < item_width)
					width = item_width;
				height += item_height + item_spacing;
				break;
			}
			if(item_height > item_highest)
				item_highest = item_height;
		}
	}
	if((width <= 0) || (height <= 0))
		return;

	/* Add one more line to describe the total number of items? */
	if((nitems > -1) ? (nitems > nitems_in_list) : FALSE)
	{
		const gchar *text = "... 123456789 items total";
		const gint swidth = gdk_string_measure(font, text);
		if(width < (swidth + 3))
			width = swidth + 3;
		height += font_height + 3 + item_spacing;
	}

	/* Add space for the drop shadow? */
	if(drop_shadow)
	{
		width += MAX(shadow_offset_x, 0);
		height += MAX(shadow_offset_y, 0);
	}

	/* Make sure that the size of the DND drag icon does not
	 * exceede the maximum
	 */
	if(width > max_width)
		width = max_width;
	if(height > max_height)
		height = max_height;

	/* Create the pixmap and mask for the DND drag icon */
	mask = GUIBitmapNew(
		window,
		width, height
	);
	pixmap = GUIPixmapNew(
		window,
		width, height,
		-1
	);
	if((mask == NULL) || (pixmap == NULL))
	{
		GDK_PIXMAP_UNREF(pixmap);
		GDK_BITMAP_UNREF(mask);
		return;
	}

	/* Create the GC for the mask */
	mask_gc = GUIGCNew((GdkWindow *)mask);
	if(mask_gc == NULL)
	{
		GDK_PIXMAP_UNREF(pixmap);
		GDK_BITMAP_UNREF(mask);
		return;
	}

	/* Clear the mask */
	gdk_gc_set_function(mask_gc, GDK_CLEAR);
	gdk_draw_rectangle(
		mask,
		mask_gc,
		TRUE,				/* Fill */
		0, 0,
		width, height
	);

#define DRAW_MASK_STRING(_xo, _yo)	{	\
 gdk_draw_string(				\
  mask,						\
  font,						\
  mask_gc,					\
  x + (_xo), y + (_yo),				\
  text						\
 );						\
}
#define DRAW_PIXMAP_STRING(_xo, _yo) {		\
 gdk_draw_string(				\
  pixmap,					\
  font,						\
  text_gc,					\
  x + (_xo), y + (_yo),				\
  text						\
 );						\
}

	/* Draw each item */
	gdk_gc_set_function(mask_gc, GDK_SET);
	gdk_gc_set_fill(mask_gc, GDK_SOLID);
	item_x = 0;
	item_y = 0;
	for(glist = cells_list;
		glist != NULL;
		glist = g_list_next(glist)
	)
	{
		/* Has the current item coordinates gone off bounds
		 * of the DND drag icon's area?
		 */
		if(layout_orientation == GTK_ORIENTATION_HORIZONTAL)
		{
			if(item_x >= max_width)
				break;
		}
		else if(layout_orientation == GTK_ORIENTATION_VERTICAL)
		{
			if(item_y >= max_height)
				break;
		}

		/* Get the information from this cell */
		switch(*(GtkCellType *)glist->data)
		{
		  case GTK_CELL_EMPTY:
		  case GTK_CELL_WIDGET:
			text = NULL;
			spacing = 0;
			icon_pixmap = NULL;
			icon_mask = NULL;
			break;
		  case GTK_CELL_TEXT:
			cell_text = (GtkCellText *)glist->data;
			text = cell_text->text;
			spacing = 0;
			icon_pixmap = NULL;
			icon_mask = NULL;
			break;
		  case GTK_CELL_PIXMAP:
			cell_pixmap = (GtkCellPixmap *)glist->data;
			text = NULL;
			spacing = 0;
			icon_pixmap = cell_pixmap->pixmap;
			icon_mask = cell_pixmap->mask;
			break;
		  case GTK_CELL_PIXTEXT:
			cell_pixtext = (GtkCellPixText *)glist->data;
			text = cell_pixtext->text;
			spacing = cell_pixtext->spacing;
			icon_pixmap = cell_pixtext->pixmap;
			icon_mask = cell_pixtext->mask;
			break;
		}
		if((icon_pixmap != NULL) && (text != NULL))
		{
			gint	x = 0, y = 0,
					item_width = 0, item_height = 0,
					lbearing = 0, rbearing, swidth = 0,
					ascent, descent;
			GdkGC *gc, *text_gc;

			/* Get the size of the icon and text */
		    gdk_window_get_size(icon_pixmap, &icon_width, &icon_height);
			gdk_string_extents(
				font, text,
				&lbearing, &rbearing, &swidth, &ascent, &descent
			);

			/* Calculate the size of this cell */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				item_width = icon_width + spacing + swidth + 3;
				item_height = MAX(icon_height, (font_height + 3));
				break;
			  case GTK_ORIENTATION_VERTICAL:
				item_width = MAX(icon_width, (swidth + 3));
				item_height = icon_height + spacing + font_height + 3;
				break;
			}

			/* Draw the icon on the mask */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				x = item_x;
				y = item_y + ((item_height - icon_height) / 2);
				break;
			  case GTK_ORIENTATION_VERTICAL:
				x = item_x + ((item_width - icon_width) / 2);
				switch(layout_orientation)
				{
				  case GTK_ORIENTATION_HORIZONTAL:
					y = item_y + ((item_highest - item_height) / 2);
					break;
				  case GTK_ORIENTATION_VERTICAL:
					y = item_y;
					break;
				}
				break;
			}
			gdk_gc_set_clip_mask(mask_gc, icon_mask);
			if(drop_shadow)
			{
				const gint	shadow_x = x + shadow_offset_x,
							shadow_y = y + shadow_offset_y;
				gdk_gc_set_clip_origin(mask_gc, shadow_x, shadow_y);
				gdk_draw_rectangle(
					mask,
					mask_gc,
					TRUE,			/* Fill */
					shadow_x, shadow_y,
					icon_width, icon_height
				);
			}
		    gdk_gc_set_clip_origin(mask_gc, x, y);
		    gdk_draw_rectangle(
		        mask,
		        mask_gc,
				TRUE,			/* Fill */
				x, y,
				icon_width, icon_height
			);
			gdk_gc_set_clip_mask(mask_gc, NULL);

			/* Draw the icon on the pixmap */
			if(drop_shadow)
				gdk_draw_rectangle(
					pixmap,
					style->black_gc,
					TRUE,			/* Fill */
					x + shadow_offset_x, y + shadow_offset_y,
					icon_width, icon_height
				);
			gc = style->white_gc;
			gdk_gc_set_clip_mask(gc, icon_mask);
		    gdk_gc_set_clip_origin(gc, x, y);
			gdk_draw_pixmap(
				pixmap,
				gc,
				icon_pixmap,
				0, 0,			/* Source x, y */
				x, y,			/* Destination x, y */
				icon_width, icon_height	/* Source size */
			);
			gdk_gc_set_clip_mask(gc, NULL);

			/* Draw the text on the mask */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				x += icon_width + spacing + 1 - lbearing;
				y = item_y + ((item_height - font_height) / 2) + 1 + font_ascent;
				break;
			  case GTK_ORIENTATION_VERTICAL:
				x = item_x + ((item_width - swidth) / 2) + 1 - lbearing;
				y += icon_height + spacing + 1 + font_ascent;
				break;
			}
			if(drop_shadow)
				DRAW_MASK_STRING(shadow_offset_x, shadow_offset_y);
			DRAW_MASK_STRING(0, 0);
			DRAW_MASK_STRING(-1, 0);
			DRAW_MASK_STRING(0, -1);
			DRAW_MASK_STRING(1, 0);
			DRAW_MASK_STRING(0, 1);

			/* Draw the text on the pixmap */
			if(drop_shadow)
			{
				text_gc = style->black_gc;
				DRAW_PIXMAP_STRING(shadow_offset_x, shadow_offset_y);
			}
			text_gc = style->base_gc[state];
			DRAW_PIXMAP_STRING(-1, 0);
			DRAW_PIXMAP_STRING(0, -1);
			DRAW_PIXMAP_STRING(1, 0);
			DRAW_PIXMAP_STRING(0, 1);
			text_gc = style->text_gc[state];
			DRAW_PIXMAP_STRING(0, 0);

			/* Increment to the next cell coordinates */
			switch(layout_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				item_x += item_width + item_spacing;
				break;
			  case GTK_ORIENTATION_VERTICAL:
				item_y += item_height + item_spacing;
				break;
			}
		}
		else if(icon_pixmap != NULL)
		{
			gint	x = 0, y = 0,
					item_width = 0, item_height = 0;
			GdkGC *gc;

			/* Get the size of this pixmap */
		    gdk_window_get_size(icon_pixmap, &icon_width, &icon_height);

			/* Calculate the size of this cell */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
			  case GTK_ORIENTATION_VERTICAL:
				item_width = icon_width;
				item_height = icon_height;
				break;
			}

			/* Draw the icon on the mask */
			x = item_x;
			y = item_y + ((item_height - icon_height) / 2);
			gdk_gc_set_clip_mask(mask_gc, icon_mask);
			if(drop_shadow)
			{
				const gint	shadow_x = x + shadow_offset_x,
							shadow_y = y + shadow_offset_y;
				gdk_gc_set_clip_origin(mask_gc, shadow_x, shadow_y);
				gdk_draw_rectangle(
					mask,
					mask_gc,
					TRUE,			/* Fill */
					shadow_x, shadow_y,
					icon_width, icon_height
				);
			}
		    gdk_gc_set_clip_origin(mask_gc, x, y);
		    gdk_draw_rectangle(
		        mask,
		        mask_gc,
				TRUE,			/* Fill */
				x, y,
				icon_width, icon_height
			);
			gdk_gc_set_clip_mask(mask_gc, NULL);

			/* Draw the icon on the pixmap */
			if(drop_shadow)
				gdk_draw_rectangle(
					pixmap,
					style->black_gc,
					TRUE,			/* Fill */
					x + shadow_offset_x, y + shadow_offset_y,
					icon_width, icon_height
				);
			gc = style->white_gc;
			gdk_gc_set_clip_mask(gc, icon_mask);
		    gdk_gc_set_clip_origin(gc, x, y);
			gdk_draw_pixmap(
				pixmap,
				gc,
				icon_pixmap,
				0, 0,			/* Source x, y */
				x, y,			/* Destination x, y */
				icon_width, icon_height	/* Source size */
			);
			gdk_gc_set_clip_mask(gc, NULL);

			/* Increment to the next cell coordinates */
			switch(layout_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				item_x += item_width + item_spacing;
				break;
			  case GTK_ORIENTATION_VERTICAL:
				item_y += item_height + item_spacing;
				break;
			}
		}
		else if(text != NULL)
		{
			gint	x = 0, y = 0,
					item_width = 0, item_height = 0,
					lbearing = 0, rbearing, swidth = 0,
					ascent, descent;
			GdkGC *text_gc;

			/* Get the size of the text */
			gdk_string_extents(
				font, text,
				&lbearing, &rbearing, &swidth, &ascent, &descent
			);

			/* Calculate the size of this cell */
			switch(icon_text_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
			  case GTK_ORIENTATION_VERTICAL:
				item_width = swidth + 3;
				item_height = font_height + 3;
				break;
			}

			/* Draw the text on the mask */
			x = item_x + 1 - lbearing;
			y = item_y + ((item_height - font_height) / 2) + 1 + font_ascent;
			if(drop_shadow)
				DRAW_MASK_STRING(shadow_offset_x, shadow_offset_y);
			DRAW_MASK_STRING(0, 0);
			DRAW_MASK_STRING(-1, 0);
			DRAW_MASK_STRING(0, -1);
			DRAW_MASK_STRING(1, 0);
			DRAW_MASK_STRING(0, 1);

			/* Draw the text on the pixmap */
			if(drop_shadow)
			{
				text_gc = style->black_gc;
				DRAW_PIXMAP_STRING(shadow_offset_x, shadow_offset_y);
			}
			text_gc = style->base_gc[state];
			DRAW_PIXMAP_STRING(-1, 0);
			DRAW_PIXMAP_STRING(0, -1);
			DRAW_PIXMAP_STRING(1, 0);
			DRAW_PIXMAP_STRING(0, 1);
			text_gc = style->text_gc[state];
			DRAW_PIXMAP_STRING(0, 0);

			/* Increment to the next cell coordinates */
			switch(layout_orientation)
			{
			  case GTK_ORIENTATION_HORIZONTAL:
				item_x += item_width + item_spacing;
				break;
			  case GTK_ORIENTATION_VERTICAL:
				item_y += item_height + item_spacing;
				break;
			}
		}
	}
	/* More items not shown? */
	if((nitems > -1) ? (nitems > nitems_in_list) : FALSE)
	{
/*	    const gint nmore_items = MAX((nitems - nitems_in_list), 0); */
		gint	x = 0, y = 0,
					lbearing = 0, rbearing, swidth = 0,
					ascent, descent;
#if 1
		gchar *text = g_strdup_printf(
			"... %i items total",
			nitems
		);
#else
		gchar *text = g_strdup_printf(
			"... %i more %s",
			nmore_items,
			(nmore_items == 1) ? "item" : "items"
		);
#endif
		GdkGC *text_gc;

		/* Get the size of the text */
		gdk_string_extents(
			font, text,
			&lbearing, &rbearing, &swidth, &ascent, &descent
		);

		/* Draw the text on the mask */
		x = ((width - swidth) / 2) + 1 - lbearing;
		y = height - (drop_shadow ? shadow_offset_y : 0) -
			font_height + 1 + font_ascent - 2;
		if(drop_shadow)
			DRAW_MASK_STRING(shadow_offset_x, shadow_offset_y);
		DRAW_MASK_STRING(0, 0);
		DRAW_MASK_STRING(-1, 0);
		DRAW_MASK_STRING(0, -1);
		DRAW_MASK_STRING(1, 0);
		DRAW_MASK_STRING(0, 1);

		/* Draw the text on the pixmap */
		if(drop_shadow)
		{
			text_gc = style->black_gc;
			DRAW_PIXMAP_STRING(shadow_offset_x, shadow_offset_y);
		}
		text_gc = style->base_gc[state];
		DRAW_PIXMAP_STRING(-1, 0);
		DRAW_PIXMAP_STRING(0, -1);
		DRAW_PIXMAP_STRING(1, 0);
		DRAW_PIXMAP_STRING(0, 1);
		text_gc = style->text_gc[state];
		DRAW_PIXMAP_STRING(0, 0);

		g_free(text);
	}

#undef DRAW_MASK_STRING
#undef DRAW_PIXMAP_STRING

	GUIDNDSetDragIcon(
		w,
		pixmap, mask,
		hot_x, hot_y
	);

	GDK_GC_UNREF(mask_gc);
	GDK_PIXMAP_UNREF(pixmap);
	GDK_BITMAP_UNREF(mask);
}

/*
 *	Sets the DND drag icon from the GtkCells list.
 *
 *	The w specifies the GtkWidget.
 *
 *	The cells_list specifies a GList of GtkCellText *,
 *	GtkCellPixmap *, or GtkCellPixText * cells. Only the first 5
 *	cells will be used.
 *
 *	The nitems specifies the total number of items, which can be
 *	greater than the number of items in cells_list. If nitems
 *	is greater than the number of items in cells_list then the
 *	number of additional items will be stated by text on the
 *	drag icon. If nitems is negative then additional items will
 *	not be stated.
 *
 *	The layout_orientation specifies whether to layout the items
 *	horizontally or vertically.
 *
 *	The icon_text_orientation specifies to either display the
 *	icon on the left and the text on the right
 *	(GTK_ORIENTATION_HORIZONTAL) or the icon on top and the text
 *	on the bottom (GTK_ORIENTATION_VERTICAL).
 *
 *	The item_spacing specifies the spacing between each item.
 */
void GUIDNDSetDragIconFromCellsList(
	GtkWidget *w,
	GList *cells_list,
	const gint nitems,
	const GtkOrientation layout_orientation,
	const GtkOrientation icon_text_orientation,
	const gint item_spacing
)
{
	if((w == NULL) || (cells_list == NULL))
		return;

	GUIDNDSetDragIconFromWidgetNexus(
		w,
		cells_list,
		nitems,
		layout_orientation,
		icon_text_orientation,
		item_spacing,
		TRUE,			/* Render drop shadow */
		GUI_DND_DRAG_ICON_DEF_HOT_X, GUI_DND_DRAG_ICON_DEF_HOT_Y
	);
}

/*
 *	Sets the DND drag icon from the GtkClist's selected rows.
 */
void GUIDNDSetDragIconFromCListSelection(GtkCList *clist)
{
	const gint	column = 0,
					max_rows = GUI_DND_DRAG_ICON_DEF_NITEMS_VISIBLE;
	gint i, row, ncolumns;
	guint8 spacing = 0;
	gchar *text = NULL;
	GList *glist, *cells_list;
	GdkPixmap *pixmap = NULL;
	GdkBitmap *mask = NULL;

	if(clist == NULL)
		return;

	ncolumns = clist->columns;
	if(column >= ncolumns)
		return;

	/* Create the cells list from the selected rows */
	cells_list = NULL;
	for(glist = clist->selection, i = 0;
		(glist != NULL) && (i < max_rows);
		glist = g_list_next(glist), i++
	)
	{
		row = (gint)glist->data;
		switch(gtk_clist_get_cell_type(clist, row, column))
		{
		  case GTK_CELL_TEXT:
			gtk_clist_get_text(
				clist,
				row, column,
				&text
			);
			if(text != NULL)
			{
				GtkCellText *cell_text = (GtkCellText *)g_malloc0(
					sizeof(GtkCellText)
				);
				if(cell_text != NULL)
				{
					cell_text->type = GTK_CELL_TEXT;
					cell_text->text = text;
					cells_list = g_list_append(
						cells_list,
						cell_text
					);
				}
			}
			break;

		  case GTK_CELL_PIXMAP:
			gtk_clist_get_pixmap(
				clist,
				row, column,
				&pixmap, &mask
			);
			if(pixmap != NULL)
			{
				GtkCellPixmap *cell_pixmap = (GtkCellPixmap *)g_malloc0(
					sizeof(GtkCellPixmap)
				);
				if(cell_pixmap != NULL)
				{
					cell_pixmap->type = GTK_CELL_PIXMAP;
					cell_pixmap->pixmap = pixmap;
					cell_pixmap->mask = mask;
					cells_list = g_list_append(
						cells_list,
						cell_pixmap
					);
				}
			}
			break;

		  case GTK_CELL_PIXTEXT:
			gtk_clist_get_pixtext(
				clist,
				row, column,
				&text,
				&spacing,
				&pixmap, &mask
			);
			if((pixmap != NULL) && (text != NULL))
			{
				GtkCellPixText *cell_pixtext = (GtkCellPixText *)g_malloc0(
					sizeof(GtkCellPixText)
				);
				if(cell_pixtext != NULL)
				{
					cell_pixtext->type = GTK_CELL_PIXTEXT;
					cell_pixtext->text = text;
					cell_pixtext->spacing = spacing;
					cell_pixtext->pixmap = pixmap;
					cell_pixtext->mask = mask;
					cells_list = g_list_append(
						cells_list,
						cell_pixtext
					);
				}
			}
			break;

		  case GTK_CELL_WIDGET:
		  case GTK_CELL_EMPTY:
			break;
		}
	}

	if(cells_list != NULL)
	{
		GUIDNDSetDragIconFromWidgetNexus(
			GTK_WIDGET(clist),
			cells_list,
			g_list_length(clist->selection),
			GTK_ORIENTATION_VERTICAL,
			GTK_ORIENTATION_HORIZONTAL,
			GUI_DND_DRAG_ICON_DEF_ITEM_SPACING,
			TRUE,			/* Render drop shadow */
			GUI_DND_DRAG_ICON_DEF_HOT_X, GUI_DND_DRAG_ICON_DEF_HOT_Y
		);
		g_list_foreach(
			cells_list,
			(GFunc)g_free,
			NULL
		);
		g_list_free(cells_list);
	}
}
	
/*
 *	Sets the DND drag icon from the GtkCList.
 *
 *	The clist specifies the GtkCList to set the DND drag icon from.
 *
 *	The row and column specifies the cell that contains the
 *	information (GdkPixmap) that will be used to set the DND drag
 *	icon from. If column is -1 then the first cell with useable
 *	information on the specified row will be used.
 */
void GUIDNDSetDragIconFromCListCell(
	GtkCList *clist,
	const gint row, const gint column
)
{
	GdkPixmap *pixmap = NULL;
	GdkBitmap *mask = NULL;

	if((clist == NULL) || (row < 0))
		return;

	if((row >= clist->rows) || (column >= clist->columns))
		return;

	/* Column specified? */
	if(column > -1)
	{
		gchar *text = NULL;
		guint8 spacing = 0;

		switch(gtk_clist_get_cell_type(clist, row, column))
		{
		  case GTK_CELL_PIXMAP:
			gtk_clist_get_pixmap(
				clist,
				row, column,
				&pixmap, &mask
			);
			break;
		  case GTK_CELL_PIXTEXT:
			gtk_clist_get_pixtext(
				clist,
				row, column,
				&text,
				&spacing,
				&pixmap, &mask
			);
			break;
		  case GTK_CELL_TEXT:
		  case GTK_CELL_WIDGET:
		  case GTK_CELL_EMPTY:
			break;
		}
	}
	else
	{
		const gint ncolumns = clist->columns;
		gint i;
		gchar *text = NULL;
		guint8 spacing = 0;

		/* Iterate through each cell of the specified row and
		 * use the first pixmap encountered
		 */
		for(i = 0; i < ncolumns; i++)
		{
			switch(gtk_clist_get_cell_type(clist, row, i))
			{
			  case GTK_CELL_PIXMAP:
				gtk_clist_get_pixmap(
					clist,
					row, i,
					&pixmap, &mask
				);
				break;
			  case GTK_CELL_PIXTEXT:
				gtk_clist_get_pixtext(
					clist,
					row, i,
					&text,
					&spacing,
					&pixmap, &mask
				);
				break;
			  case GTK_CELL_TEXT:
			  case GTK_CELL_WIDGET:
			  case GTK_CELL_EMPTY:
				break;
			}
			if(pixmap != NULL)
				break;
		}
	}

	GUIDNDSetDragIcon(
		GTK_WIDGET(clist),
		pixmap, mask,
		GUI_DND_DRAG_ICON_DEF_HOT_X, GUI_DND_DRAG_ICON_DEF_HOT_Y
	);
}

/*
 *	Sets the DND drag icon from the GtkCList's selected row.
 *
 *	The clist specifies the GtkCList to set the DND drag icon from.
 *
 *	The last selected row with the first cell with useable
 *	information (GdkPixmap) will be used. If no rows are selected
 *	then no DND drag icon will be set.
 */
void GUIDNDSetDragIconFromCList(GtkCList *clist)
{
	GList *glist;

	if(clist == NULL)
		return;

	glist = clist->selection_end;
	if(glist == NULL)
		return;

	GUIDNDSetDragIconFromCListCell(
		clist,
		(gint)glist->data,
		-1					/* Use the first useable cell */
	);
}

/*
 *	Sets the DND drag icon from the GtkCTree's selected nodes.
 */
void GUIDNDSetDragIconFromCTreeSelection(GtkCTree *ctree)
{
	gboolean	is_leaf = FALSE,
					expanded = FALSE;
	const gint max_rows = GUI_DND_DRAG_ICON_DEF_NITEMS_VISIBLE;
	gint i;
	gchar *text = NULL;
	guint8 spacing = 0;
	GdkBitmap	*mask = NULL,
					*mask_closed = NULL,
					*mask_opened = NULL;
	GdkPixmap	*pixmap = NULL,
					*pixmap_closed = NULL,
					*pixmap_opened = NULL;
	GList *glist, *cells_list;
	GtkCList *clist;
	GtkCTreeNode *node;

	if(ctree == NULL)
		return;

	clist = GTK_CLIST(ctree);

	/* Create the cells list from the selected rows */
	cells_list = NULL;
	for(glist = clist->selection, i = 0;
		(glist != NULL) && (i < max_rows);
		glist = g_list_next(glist), i++
	)
	{
		/* Get the next selected node */
		node = (GtkCTreeNode *)glist->data;
		if(node == NULL)
			continue;

		gtk_ctree_get_node_info(
			ctree,
			node,
			&text,
			&spacing,
			&pixmap_closed,
			&mask_closed,
			&pixmap_opened,
			&mask_opened,
			&is_leaf,
			&expanded
		);
		if((text != NULL) || (pixmap_closed != NULL))
		{
			/* Is the node opened? */
			if(expanded)
			{
				/* Get the opened GdkPixmap */
				pixmap = pixmap_opened;
				mask = mask_opened;

				/* If the opened GdkPixmap was not available then
				 * get the closed GdkPixmap
				 */
				if(pixmap == NULL)
				{
					pixmap = pixmap_closed;
					mask = mask_closed;
				}
			}
			else
			{
				/* Get the closed GdkPixmap */
				pixmap = pixmap_closed;
				mask = mask_closed;
			}

			if((text != NULL) && (pixmap != NULL))
			{
				GtkCellPixText *cell_pixtext = (GtkCellPixText *)g_malloc0(
					sizeof(GtkCellPixText)
				);
				if(cell_pixtext != NULL)
				{
					cell_pixtext->type = GTK_CELL_PIXTEXT;
					cell_pixtext->text = text;
					cell_pixtext->spacing = spacing;
					cell_pixtext->pixmap = pixmap;
					cell_pixtext->mask = mask;
					cells_list = g_list_append(
						cells_list,
						cell_pixtext
					);
				}
			}
			else if(pixmap != NULL)
			{
				GtkCellPixmap *cell_pixmap = (GtkCellPixmap *)g_malloc0(
					sizeof(GtkCellPixmap)
				);
				if(cell_pixmap != NULL)
				{
					cell_pixmap->type = GTK_CELL_PIXMAP;
					cell_pixmap->pixmap = pixmap;
					cell_pixmap->mask = mask;
					cells_list = g_list_append(
						cells_list,
						cell_pixmap
					);
				}
			}
			else if(text != NULL)
			{
				GtkCellText *cell_text = (GtkCellText *)g_malloc0(
					sizeof(GtkCellText)
				);
				if(cell_text != NULL)
				{
					cell_text->type = GTK_CELL_TEXT;
					cell_text->text = text;
					cells_list = g_list_append(
						cells_list,
						cell_text
					);
				}
			}
		}
	}

	if(cells_list != NULL)
	{
		GUIDNDSetDragIconFromWidgetNexus(
			GTK_WIDGET(ctree),
			cells_list,
			g_list_length(clist->selection),
			GTK_ORIENTATION_VERTICAL,
			GTK_ORIENTATION_HORIZONTAL,
			GUI_DND_DRAG_ICON_DEF_ITEM_SPACING,
			TRUE,			/* Render drop shadow */
			GUI_DND_DRAG_ICON_DEF_HOT_X, GUI_DND_DRAG_ICON_DEF_HOT_Y
		);
		g_list_foreach(
			cells_list,
			(GFunc)g_free,
			NULL
		);
		g_list_free(cells_list);
	}
}

/*
 *	Sets the DND drag icon from the GtkCTree node.
 *
 *	The ctree specifies the GtkCTree to set the DND drag icon from.
 *
 *	The node and column specifies the cell that contains the
 *	information (GdkPixmap) that will be used to set the DND drag
 *	icon from. If column is -1 then the first cell with useable
 *	information on the specified row will be used.
 */
void GUIDNDSetDragIconFromCTreeNode(
	GtkCTree *ctree,
	GtkCTreeNode *node,
	const gint column
)
{
	GdkPixmap *pixmap = NULL;
	GdkBitmap *mask = NULL;
	GtkCList *clist;

	if((ctree == NULL) || (node == NULL))
		return;

	clist = GTK_CLIST(ctree);
	if(column >= clist->columns)
		return;

	/* No column specified? */
	if(column < 0)
	{
		/* Use the first cell */
		GtkCTreeRow *row_ptr = GTK_CTREE_ROW(node);
		if(row_ptr != NULL)
		{
			/* Is the node opened? */
			if(row_ptr->expanded)
			{
				/* Get the opened GdkPixmap */
				pixmap = row_ptr->pixmap_opened;
				mask = row_ptr->mask_opened;

				/* If the opened GdkPixmap was not available then
				 * get the closed GdkPixmap
				 */
				if(pixmap == NULL)
				{
					pixmap = row_ptr->pixmap_closed;
					mask = row_ptr->mask_closed;
				}
			}
			else
			{
				/* Get the closed GdkPixmap */
				pixmap = row_ptr->pixmap_closed;
				mask = row_ptr->mask_closed;
			}
		}
	}
	else
	{
		gchar *text = NULL;
		guint8 spacing = 0;

		switch(gtk_ctree_node_get_cell_type(ctree, node, column))
		{
		  case GTK_CELL_PIXMAP:
			gtk_ctree_node_get_pixmap(
				ctree,
				node, column,
				&pixmap, &mask
			);
			break;
		  case GTK_CELL_PIXTEXT:
			gtk_ctree_node_get_pixtext(
				ctree,
				node, column,
				&text,
				&spacing,
				&pixmap, &mask
			);
			break;
		  case GTK_CELL_TEXT:
		  case GTK_CELL_WIDGET:
		  case GTK_CELL_EMPTY:
			break;
		}
	}

	GUIDNDSetDragIcon(
		GTK_WIDGET(ctree),
		pixmap, mask,
		GUI_DND_DRAG_ICON_DEF_HOT_X, GUI_DND_DRAG_ICON_DEF_HOT_Y
	);
}

/*
 *	Sets the GtkWidget's DND drag icon.
 *
 *	The w specifies the GtkWidget.
 *
 *	The pixmap and mask specifies the GdkPixmap and GdkBitmap to
 *	use as the new DND drag icon. If pixmap is NULL then the
 *	DND drag icon is cleared.
 *
 *	The hot_x and hot_y specifies the hot spot.
 */
void GUIDNDSetDragIcon(
	GtkWidget *w,
	GdkPixmap *pixmap, GdkBitmap *mask,
	const gint hot_x, const gint hot_y
)
{
	gboolean is_pixmap_set = FALSE;
	GdkWindow *window;
	GUIDNDDragIcon *dnd_drag_icon;

	if(w == NULL)
		return;

	dnd_drag_icon = GUI_DND_DRAG_ICON(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_DND_DRAG_ICON_KEY
	));
	if(dnd_drag_icon == NULL)
	{
		dnd_drag_icon = GUI_DND_DRAG_ICON(g_malloc0(
			sizeof(GUIDNDDragIcon)
		));
		if(dnd_drag_icon == NULL)
			return;

		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_DND_DRAG_ICON_KEY,
			dnd_drag_icon, GUIDNDDragIconDestroyCB
		);
	}

#if 0
/* This can occur normally when a drag icon needs to be changed
 * during a drag
 */
	if(dnd_drag_icon->ref_count > 0)
		g_printerr(
"GUIDNDSetDragIcon(): Warning: Setting dnd_drag_icon=%p with\
 %i reference counts remaining.\n",
			dnd_drag_icon,
			dnd_drag_icon->ref_count
		);
#endif

	/* Clear the DND drag icon? */
	if(pixmap == NULL)
	{
		/* Destroy the DND drag icon GtkWidget */
		GTK_WIDGET_DESTROY(dnd_drag_icon->toplevel);
		memset(dnd_drag_icon, 0x00, sizeof(GUIDNDDragIcon));
		return;
	}

	/* If the DND drag icon GtkWidget does not exist then this
	 * the very first call to this function
	 */
	w = dnd_drag_icon->toplevel;
	if(w == NULL)
	{
		/* Create the DND drag icon GtkWidget */
		GtkWidget *parent;

		dnd_drag_icon->toplevel = w = gtk_window_new(GTK_WINDOW_POPUP);
		gtk_window_set_policy(GTK_WINDOW(w), FALSE, FALSE, FALSE);
		parent = w;

		dnd_drag_icon->icon_pm = w = gtk_pixmap_new(pixmap, mask);
		gtk_container_add(GTK_CONTAINER(parent), w);
		gtk_widget_show(w);
		is_pixmap_set = TRUE;
	}

	/* Get the DND drag icon's toplevel GdkWindow */
	w = dnd_drag_icon->toplevel;
	if(!GTK_WIDGET_REALIZED(w))
		gtk_widget_realize(w);
	window = w->window;
	if(window == NULL)
		return;

	/* Set the hot spot */
	dnd_drag_icon->x = hot_x;
	dnd_drag_icon->y = hot_y;

	/* Set the size of the new DND drag icon */
	gdk_window_get_size(
		pixmap,
		&dnd_drag_icon->width, &dnd_drag_icon->height
	);

	/* Adjust the size and shape of the DND drag icon's toplevel
	 * GdkWindow
	 */
	gtk_widget_set_usize(
		w,
		dnd_drag_icon->width, dnd_drag_icon->height
	);
	gdk_window_set_back_pixmap(
		window,
		pixmap,
		FALSE				/* Not parent relative */
	);
	gtk_widget_shape_combine_mask(
		w,
		mask,
		0, 0				/* Offset */
	);

	/* Need to set the icon on to the GtkPixmap? */
	if(!is_pixmap_set)
		gtk_pixmap_set(
			GTK_PIXMAP(dnd_drag_icon->icon_pm),
			pixmap, mask
		);
}

/*
 *	Clears the GtkWidget's DND drag icon.
 *
 *	The w specifies the GtkWidget.
 */
void GUIDNDClearDragIcon(GtkWidget *w)
{
	GUIDNDSetDragIcon(
		w,
		NULL, NULL,
		0, 0
	);
}


/*
 *	Creates a new Banner.
 */
GtkWidget *GUIBannerCreate(
	const gchar *label,
	GdkFont *font,
	GdkColor *color_fg,
	GdkColor *color_bg,
	GdkColor *color_border,
	const GtkJustification justify,
	const gboolean expand,
	const gint inside_border,
	const gint outside_border
)
{
	gint width, height;
	GtkWidget *w;
	GUIBannerData *banner = GUI_BANNER_DATA(g_malloc0(
		sizeof(GUIBannerData)
	));
	if(banner == NULL)
		return(NULL);

	banner->toplevel = gtk_drawing_area_new();
	banner->label = STRDUP(label);
	banner->font = GDK_FONT_REF(font);
	if(color_fg != NULL)
		banner->color_fg = g_memdup(color_fg, sizeof(GdkColor));
	if(color_bg != NULL)
		banner->color_bg = g_memdup(color_bg, sizeof(GdkColor));
	if(color_border != NULL)
		banner->color_border = g_memdup(color_border, sizeof(GdkColor));
	banner->justify = justify;
	banner->inside_border = inside_border;
	banner->outside_border = outside_border;

	w = banner->toplevel;
	gtk_object_set_data_full(
		GTK_OBJECT(w), GUI_BANNER_DATA_KEY,
		banner, GUIBannerDestroyCB
	);
	gtk_widget_add_events(
		w,
		GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK
	);
	gtk_signal_connect(
		GTK_OBJECT(w), "realize",
		GTK_SIGNAL_FUNC(GUIBannerRealizeCB), banner
	);
	gtk_signal_connect(
		GTK_OBJECT(w), "configure_event",
		GTK_SIGNAL_FUNC(GUIBannerEventCB), banner
	);
	gtk_signal_connect(
		GTK_OBJECT(w), "expose_event",
		GTK_SIGNAL_FUNC(GUIBannerEventCB), banner
	);

	/* Set the size */
	if(expand)
		width = -1;
	else if((font != NULL) && (label != NULL))
		width = gdk_string_measure(font, label) +
			(2 * inside_border) + (2 * outside_border);
	else
		width = -1;
	height = ((font != NULL) ? (font->ascent + font->descent) : 1) +
		(2 * inside_border) + (2 * outside_border);
	gtk_widget_set_usize(w, width, height);

	return(banner->toplevel);
}

/*
 *	Draws the Banner.
 */
void GUIBannerDraw(GtkWidget *w)
{
	gint		width, height,
					inside_border,
					outside_border;
	const gchar *label;
	GdkFont *font;
	GdkWindow *window;
	GdkDrawable *drawable;
	GdkGC *gc;
	GtkStateType state;
	GtkJustification justify;
	GtkStyle *style;
	GUIBannerData *banner = GUI_BANNER_DATA(GTK_OBJECT_GET_DATA(
		w,
		GUI_BANNER_DATA_KEY
	));
	if(banner == NULL)
		return;

	label = banner->label;
	justify = banner->justify;
	inside_border = banner->inside_border;
	outside_border = banner->outside_border;
	font = banner->font;

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

	drawable = (GdkDrawable *)window;

	gdk_window_get_size(
		(GdkWindow *)drawable,
		&width, &height
	);
	if((width <= 0) || (height <= 0))
		return;

	/* Border */
	if(outside_border > 0)
	{
		gdk_gc_set_foreground(
			gc,
			(banner->color_border != NULL) ?
				banner->color_border :
				&style->fg[state]
		);
		gdk_draw_rectangle(
			drawable,
			gc,
			TRUE,				/* Fill */
			0, 0,
			width, height
		);
	}

	/* Background */
	gdk_gc_set_foreground(
		gc,
		(banner->color_bg != NULL) ?
			banner->color_bg :
			&style->bg[state]
	);
	gdk_draw_rectangle(
		drawable,
		gc,
		TRUE,				/* Fill */
		0 + outside_border,
		0 + outside_border,
		width - (2 * outside_border),
		height - (2 * outside_border)
	);

	/* Label */
	if((font != NULL) && !STRISEMPTY(label))
	{
		gint lbearing, rbearing, swidth, ascent, descent;

		gdk_gc_set_foreground(
			gc,
			(banner->color_fg != NULL) ?
				banner->color_fg :
				&style->text[state]
		);
		gdk_gc_set_font(gc, font);
		gdk_string_extents(
			font,
			label,
			&lbearing, &rbearing, &swidth, &ascent, &descent
		);
		switch(justify)
		{
		  case GTK_JUSTIFY_CENTER:
			gdk_draw_string(
				drawable,
				font,
				gc,
				((width - swidth) / 2) - lbearing,
				((height - (font->ascent + font->descent)) / 2) +
					font->ascent,
				label  
			);
			break;
		  case GTK_JUSTIFY_RIGHT:
			gdk_draw_string(
				drawable,
				font,
				gc,
				(width - swidth - (2 * outside_border) - (2 * inside_border)) - lbearing,
				((height - (font->ascent + font->descent)) / 2) +
					font->ascent,
				label
			);
			break;
		  case GTK_JUSTIFY_LEFT:
			gdk_draw_string(
				drawable,
				font,
				gc,
				((2 * outside_border) + (2 * inside_border)) - lbearing,
				((height - (font->ascent + font->descent)) / 2) +
					font->ascent,
				label                    
			);
			break;
		  case GTK_JUSTIFY_FILL:
			break;
		}
	}

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


/*
 *	Creates a new GtkCombo with label, combo, initial value, and
 *	initial list of items.
 *
 *	Returns the GtkTable (the parent) or NULL on failure.
 *
 *	The given GList list will be duplicated, so the one passed to
 *	this call will not be modified.
 */
GtkWidget *GUIComboCreate(
	const gchar *label,	/* Label */
	const gchar *text,	/* Entry Value */
	GList *list,		/* Combo List */
	gint max_items,		/* Maximum Items In Combo List */
	gpointer *combo_rtn,	/* GtkCombo Return */
	gpointer data,
	void (*func_cb)(GtkWidget *w, gpointer),
	void (*list_change_cb)(GtkWidget *, gpointer, GList *)
)
{
	GtkWidget *parent, *wlabel = NULL, *w;
	GtkCombo *combo;
	const gchar *cstrptr;
	gpointer *cb_data;


	/* Reset return values */
	if(combo_rtn != NULL)
		*combo_rtn = NULL;

	/* Create parent table widget */
	parent = gtk_table_new(
		1,
		((label != NULL) ? 1 : 0) +
		1,
		FALSE
	);
	/* Do not show the table parent */

	/* Create label? */
	if(label != NULL)
	{
		gint x = 0;

		wlabel = gtk_label_new(label);
		gtk_table_attach(
			GTK_TABLE(parent), wlabel,
			(guint)x, (guint)(x + 1),
			0, 1,
			0,
			0,
			2, 2
		);
		gtk_label_set_justify(GTK_LABEL(wlabel), GTK_JUSTIFY_RIGHT);
		gtk_widget_show(wlabel);
	}

	/* Create combo */
	if(TRUE)
	{
		gint i;
		gint x = (label != NULL) ? 1 : 0;
		GList *glist_in, *glist_out;
		GtkEntry *entry;


		w = gtk_combo_new();
		combo = GTK_COMBO(w);
		entry = GTK_ENTRY(combo->entry);
		if(combo_rtn != NULL)
			*combo_rtn = w;
		gtk_table_attach(
			GTK_TABLE(parent), w,
			(guint)x, (guint)(x + 1),
			0, 1,
			GTK_SHRINK | GTK_EXPAND | GTK_FILL,
			0,
			2, 2
		);
		gtk_combo_disable_activate(combo);
		gtk_entry_set_text(entry, (text != NULL) ? text : "");
		gtk_entry_set_editable(entry, TRUE);
		gtk_combo_set_case_sensitive(combo, FALSE);


		/* Begin creating output glist, a duplicate input glist */
		glist_out = NULL;
		glist_in = (GList *)list;

		/* Is input glist given? */
		if(glist_in == NULL)
		{
			/* Input glist is NULL so create a bunch of empty strings
			 * totalling that specified by max_items for the output
			 * glist.
			 */
			for(i = 0; i < max_items; i++)
				glist_out = g_list_append(glist_out, STRDUP(""));
		}
		else
		{
			/* Input glist is given, make a duplicate of it as the
			 * output glist.
			 */
			i = 0;
			while((i < max_items) && (glist_in != NULL))
			{
				cstrptr = (const gchar *)glist_in->data;
				glist_out = g_list_append(
					glist_out, STRDUP(cstrptr)
				);
				glist_in = g_list_next(glist_in);
				i++;
			}
		}
		glist_in = NULL;	/* Input glist should no longer be used */

		/* Set new output glist to combo box */
		if(glist_out != NULL)
			gtk_combo_set_popdown_strings(combo, glist_out);

		/* Do not delete the output glist, it will be passed to the
		 * callback data below.
		 */

		/* Allocate callback data, 9 pointers of format;
		 * table		Parent table that holds this combo and label.
		 * label		Label (may be NULL).
		 * combo		This combo widget.
		 * GList		Contains list of strings in combo list.
		 * max_items	Max items allowed in combo list.
		 * data		Calling function set callback data.
		 * func_cb		Calling function set callback function.
		 * list_change_cb	Calling function set list change function.
		 * NULL
		 */
		cb_data = (gpointer *)g_malloc0(9 * sizeof(gpointer));
		cb_data[0] = parent;
		cb_data[1] = wlabel;
		cb_data[2] = combo;
		cb_data[3] = glist_out;
		cb_data[4] = (gpointer)MAX(max_items, 0);
		cb_data[5] = data;
		cb_data[6] = (gpointer)func_cb;
		cb_data[7] = (gpointer)list_change_cb;
		cb_data[8] = NULL;
		gtk_object_set_data_full(
			GTK_OBJECT(combo), GUI_COMBO_DATA_KEY,
			cb_data, GUIComboDataDestroyCB
		);

		gtk_signal_connect(
			GTK_OBJECT(entry), "activate",
			GTK_SIGNAL_FUNC(GUIComboActivateCB), cb_data
		);
		gtk_signal_connect(
			GTK_OBJECT(entry), "changed",
			GTK_SIGNAL_FUNC(GUIComboChangedCB), cb_data
		);

		gtk_widget_show(w);
	}

	return(parent);
}

/*
 *	Adds the specified value to the combo widget w's list and current
 *	value for the combo widget's entry widget.
 *
 *	The item will only be added to the list if it is not NULL
 *	and does not already exist in the list.
 *
 *	Excess items may be truncated if adding this item would exceed
 *	the combo list's maximum allowed items.
 */
void GUIComboActivateValue(GtkWidget *w, const gchar *value)
{
	gpointer *cb_data;
	GtkCombo *combo = (GtkCombo *)w;
	if((combo == NULL) || (value == NULL))
		return;

	cb_data = (gpointer *)gtk_object_get_data(
		GTK_OBJECT(combo),
		GUI_COMBO_DATA_KEY
	);
	if(cb_data == NULL)
		return;

	/* Set new value on combo's entry widget */
	gtk_entry_set_text(
		GTK_ENTRY(combo->entry), value
	);

	/* Call activate callback as if this was an actual activate
	 * signal
	 */
	GUIComboActivateCB(GTK_WIDGET(combo), (gpointer)cb_data);
}

/*
 *	Adds the given value to the beginning of the glist for the
 *	combo box w. Older items in the list may be truncated if adding
 *	a new value would exceed the set maximum items for the combo box.
 *
 *	If value is already in the list then no operation will be
 *	performed.
 *
 *	The combo box's set list change callback will be called if it is
 *	not NULL and the new value was different and added succesfully.
 */
void GUIComboAddItem(GtkWidget *w, const gchar *value)
{
	gint i, max_items;
	gboolean status;
	const gchar *cstrptr;
	GtkWidget *parent, *wlabel;
	GtkCombo *combo = (GtkCombo *)w;
	GList *glist_in, *glist_next_in;
	gpointer client_data;
	void (*func_cb)(GtkWidget *, gpointer);
	void (*list_change_cb)(GtkWidget *, gpointer, GList *);
	gpointer *cb_data;
	if((combo == NULL) || (value == NULL))
		return;

	/* Get object callback data */
	cb_data = (gpointer *)gtk_object_get_data(
		GTK_OBJECT(combo),
		GUI_COMBO_DATA_KEY
	);
	if(cb_data == NULL)
		return;

	/* Parse callback data, format is as follows;
	 * table        Parent table that holds this combo and label.
	 * label        Label (may be NULL).
	 * combo        This combo widget.
	 * GList        Contains list of strings in combo list.
	 * max_items    Max items allowed in combo list.
	 * client_data  Calling function set callback data.
	 * func_cb      Calling function set callback function.
	 * list_change_cb       Calling function set list change function.
	 * NULL
	 */
	parent = (GtkWidget *)(cb_data[0]);
	wlabel = (GtkWidget *)(cb_data[1]);
	combo = (GtkCombo *)(cb_data[2]);
	glist_in = (GList *)(cb_data[3]);
	max_items = (gint)(cb_data[4]);
	client_data = (gpointer)(cb_data[5]);
	func_cb = cb_data[6];
	list_change_cb = cb_data[7];


	/* Check if new value is already in the input glist */
	status = TRUE;
	i = 0;
	glist_next_in = glist_in;
	/* Iterate from 0 to max_items or when the input glist is
	 * NULL (whichever occures first).
	 */
	while((i < max_items) && (glist_next_in != NULL))
	{
		cstrptr = (gchar *)glist_next_in->data;
		if(cstrptr != NULL)
		{
 /* Check if case sensitive? */
			/* Compare list item value with given value */
			if(!g_strcasecmp(cstrptr, value))
			{
				/* Given value matches a value in the list, so set
				 * status to FALSE indicating there is a value already
				 * in the list.
				 */
				status = FALSE;
				break;
			}
		}
		i++;
		glist_next_in = g_list_next(glist_next_in);
	}
	/* Variable status will be set to FALSE if the value is already
	 * in the list.
	 */

	/* Check if max_items allows us to add a new item to the list and
	 * if status is TRUE (indicating value is not already in the
	 * list).
	 */
	if((max_items > 0) && status)
	{ 
		/* Create new output glist */
		GList *glist_out = NULL;

		/* Add first item in output glist to be the new value fetched
		 * from the combo's entry.
		 */
		glist_out = g_list_append(glist_out, STRDUP(value));
	
		/* Now copy old input glist items to output glist, starting
		 * with i = 1 since we already have one item in the output
		 * glist.
		 */
		i = 1;
		glist_next_in = glist_in;
		while((i < max_items) && (glist_next_in != NULL)) 
		{
			cstrptr = (const gchar *)glist_next_in->data;
			glist_out = g_list_append(glist_out, STRDUP(cstrptr));
			glist_next_in = g_list_next(glist_next_in);
			i++;
		}

		/* Set new output glist to the combo box's list */
		if(glist_out != NULL)
			gtk_combo_set_popdown_strings(combo, glist_out);

		/* Free old input glist since its no longer being used */
		if(glist_in != NULL)
		{
			g_list_foreach(glist_in, (GFunc)g_free, NULL);
			g_list_free(glist_in);
			glist_in = NULL;
		}

		/* Update input glist to be that of the output glist */
		glist_in = glist_out;

		/* Record new glist on callback data */
		cb_data[3] = (gpointer)glist_in;

		/* Call list change function and pass the new glist */
		if(list_change_cb != NULL)
		{
			list_change_cb(
				(GtkWidget *)combo,	/* Pass combo box as widget */
				client_data,	/* Client data */
				glist_in		/* New glist */
			);
		}
	}
}

/*
 *	Returns the GList for the combo widget created by
 *	GUIComboCreate().
 */
GList *GUIComboGetList(GtkWidget *w)
{
	gpointer *cb_data;

	if(w == NULL)
		return(NULL);

	cb_data = (gpointer *)gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_COMBO_DATA_KEY
	);
	if(cb_data == NULL)
		return(NULL);

	/* Parse callback data, format is as follows;
	 * table            Parent table that holds this combo and label.
	 * label            Label (may be NULL).
	 * combo            This combo widget.
	 * GList            Contains list of strings in combo list.
	 * max_items        Max items allowed in combo list.
	 * client_data      Calling function set callback data.
	 * func_cb          Calling function set callback function.
	 * list_change_cb   Calling function set list change function.
	 * NULL
	 */

	return((GList *)cb_data[3]);
}

/*
 *	Sets the new list for the combo.
 *
 *	If the pointer of the given list is the same as the combo's
 *	current list then the given list will simply be pdated to the
 *	combo with a call to gtk_combo_set_popdown_strings().
 *
 *	If the given list is NULL then the combo's current list will
 *	be deleted.
 *
 *	In any case the given list should not be referenced again after
 *	this call.
 */
void GUIComboSetList(GtkWidget *w, GList *list)
{
	GList *glist;
	GtkCombo *combo;
	gpointer *cb_data;

	if(w == NULL)
		return;

	combo = GTK_COMBO(w);
	if(combo == NULL)
		return;

	cb_data = (gpointer *)gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_COMBO_DATA_KEY
	);
	if(cb_data == NULL)
		return;

	/* Parse callback data, format is as follows;
	 * table            Parent table that holds this combo and label.
	 * label            Label (may be NULL).
	 * combo            This combo widget.
	 * GList            Contains list of strings in combo list.
	 * max_items        Max items allowed in combo list.
	 * client_data      Calling function set callback data.
	 * func_cb          Calling function set callback function.
	 * list_change_cb   Calling function set list change function.
	 * NULL
	 */
	glist = (GList *)cb_data[3];

	/* Is given list NULL? */
	if(list == NULL)
	{
		gint i, max_items = (gint)cb_data[4];
		GList	*glist_new,
					*glist_old = glist;

		/* Create a new glist containing just empty strings */
		glist_new = NULL;
		for(i = 0; i < max_items; i++)
			glist_new = g_list_append(glist_new, STRDUP(""));

		/* Was new glist created successfully? */
		if(glist_new != NULL)
		{
			/* Set new glist to combo */
			gtk_combo_set_popdown_strings(combo, glist_new);

			/* If old glist exists, then delete it */
			if(glist_old != NULL)
			{
				g_list_foreach(glist_old, (GFunc)g_free, NULL);
				g_list_free(glist_old);
				glist_old = NULL;
			}

			/* Update pointer to new glist */
			cb_data[3] = (gpointer)glist_new;
			glist = glist_new;
		}
	}
	/* Given list matches current list's base pointer values? */
	else if((gpointer)list == (gpointer)glist)
	{
		/* Just update list on combo then */
		gtk_combo_set_popdown_strings(combo, glist);

		/* No need to change pointer on callback data */

		/* No need to deallocate given list */
	}
	else
	{
		/* New and current list base pointers do not match and
		 * current glist may be NULL
		 */
		const gchar *s;
		gint i, max_items;
		GList	*glist_new, *glist_in,
					*glist_old = glist;

		/* Make a copy the given list as glist_new and limit the
		 * number of items to max_items
		 */
		i = 0;
		max_items = (gint)cb_data[4];
		glist_new = NULL;		/* New glist */
		glist_in = (GList *)list;	/* Input glist */
		while((i < max_items) && (glist_in != NULL))
		{
			s = (const gchar *)glist_in->data;
			glist_new = g_list_append(glist_new, STRDUP(s));
			glist_in = g_list_next(glist_in);
			i++;
		}

		/* Destroy the given list */
		glist_in = (GList *)list;	/* Input glist */
		list = NULL;		/* Mark input list as NULL */
		if(glist_in != NULL)
		{
			g_list_foreach(glist_in, (GFunc)g_free, NULL);
			g_list_free(glist_in);
			glist_in = NULL;
		}

		/* Is new coppied glist valid? */
		if(glist_new != NULL)
		{
			/* Set new glist to combo */
			gtk_combo_set_popdown_strings(combo, glist_new);

			/* If old glist exists, then delete it */
			if(glist_old != NULL)
			{ 
				g_list_foreach(glist_old, (GFunc)g_free, NULL);
				g_list_free(glist_old);
				glist_old = NULL;
			}

			/* Update pointer to new glist on callback data */
			cb_data[3] = (gpointer)glist_new;
			glist = glist_new;
		}
	}
}

/*
 *	Resets all values in the combo widget.
 */
void GUIComboClearAll(GtkWidget *w)
{
	GtkCombo *combo = (GtkCombo *)w;
	if(combo == NULL)
		return;

	/* Clear text entry */
 	gtk_entry_set_text(GTK_ENTRY(combo->entry), "");
	gtk_entry_set_position(GTK_ENTRY(combo->entry), 0);

	/* Clear combo's glist by setting a new one as NULL */
	GUIComboSetList(w, NULL);
}


/*
 *	Creates a new Menu Bar & GtkAccelGroup.
 */
GtkWidget *GUIMenuBarCreate(GtkAccelGroup **accelgrp_rtn)
{
	if(accelgrp_rtn != NULL)
		*accelgrp_rtn = gtk_accel_group_new();

	return(gtk_menu_bar_new());
}

/*
 *	Creates a new Tear Off Menu.
 */
GtkWidget *GUIMenuCreateTearOff(void)
{
	GtkWidget	*menu = gtk_menu_new(),
					*w = gtk_tearoff_menu_item_new();
	gtk_menu_append(GTK_MENU(menu), w);
	gtk_widget_show(w);
	return(menu);
}

/*
 *	Creates a New Menu.
 */
GtkWidget *GUIMenuCreate(void)
{
	return(gtk_menu_new());
}

/*
 *	Creates a new Menu Item.
 *
 *	If accelgrp is NULL then only the accelerator key label will
 *	state the accelerator key and the accelerator key will not be
 *	added to the accelerator group.
 */
GtkWidget *GUIMenuItemCreate(
	GtkWidget *menu,
	const gui_menu_item_type type,
	GtkAccelGroup *accelgrp,
	guint8 **icon,
	const gchar *label,
	const guint accel_key, const guint accel_mods,
	void (*func_cb)(GtkWidget *w, gpointer),
	gpointer data
)
{
	const gint	border_minor = 2,
					border_accel_label = 15;
	gint		ncolumns, nrows,
					attach_x, attach_y;
	GtkWidget	*w,
					*menu_item = NULL,
					*parent, *parent2;
	GUIMenuItemData *mi;

	if(menu == NULL)
		return(NULL);

	/* Create the menu item data */
	mi = GUI_MENU_ITEM_DATA(g_malloc0(sizeof(GUIMenuItemData)));
	if(mi == NULL)
		return(NULL);

	/* Create the menu item by type */
	switch(type)
	{
	  case GUI_MENU_ITEM_TYPE_LABEL:
	  case GUI_MENU_ITEM_TYPE_SUBMENU:
		/* Create the GtkMenuItem */
		mi->menu_item = menu_item = parent = w = gtk_menu_item_new();
		gtk_widget_add_events(
			w,
			GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
		);
		gtk_menu_append(GTK_MENU(menu), w);
		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_MENU_ITEM_DATA_KEY,
			mi, GUIMenuItemDataDestroyCB
		);
		gtk_widget_show(w);
		/* GtkTable for the icon GtkPixmap, main GtkLabel, and
		 * accelkey GtkLabel
		 */
		ncolumns = 3;
		nrows = 1;
		w = gtk_table_new(
			nrows, ncolumns,
			FALSE
		);
		attach_x = 0;
		attach_y = 0;
		gtk_container_add(GTK_CONTAINER(parent), w);
#if !defined(_WIN32)
		gtk_table_set_col_spacing(
			GTK_TABLE(w),
			0,
			(guint)border_minor
		);
		gtk_table_set_col_spacing(
			GTK_TABLE(w),
			ncolumns - 2,
			(guint)border_accel_label
		);
#endif
		gtk_widget_show(w);
		parent = w;

		/* Create the icon, if the icon is NULL then an empty
		 * fixed widget would have been returned
		 */
		mi->icon_data = (const guint8 **)icon;
		mi->pixmap = w = GUICreateMenuItemIcon(icon);
		if(w != NULL)
		{
			gtk_table_attach(
				GTK_TABLE(parent),
				w,
				attach_x, attach_x + 1,
				attach_y, attach_y + 1,
				0,
				GTK_EXPAND | GTK_FILL,
				0, 0
			);
			attach_x++;
			gtk_widget_show(w);
		}

		/* GtkAlignment for the msin GtkLabel */
		w = gtk_alignment_new(0.0f, 0.5f, 0.0f, 0.0f);
		gtk_table_attach(
			GTK_TABLE(parent),
			w,
			attach_x, attach_x + 1,
			attach_y, attach_y + 1,
			GTK_SHRINK | GTK_EXPAND | GTK_FILL,
			GTK_EXPAND | GTK_FILL,
			0, 0
		);
		attach_x++;
		gtk_widget_show(w);
		parent2 = w;
		/* Create the GtkLabel */
		mi->label_text = g_strdup((label != NULL) ? label : "");
		mi->label = w = gtk_label_new(mi->label_text);
		gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
		gtk_container_add(GTK_CONTAINER(parent2), w);
		gtk_widget_show(w);

		/* Set the callback data and signals for this GtkMenuItem */
		if(func_cb != NULL)
		{
			GUIMenuItemActivateData *d = GUI_MENU_ITEM_ACTIVATE_DATA(g_malloc0(
				sizeof(GUIMenuItemActivateData)
			));
			if(d != NULL)
			{
				d->w = menu_item;
				d->cb = func_cb;
				d->data = data;
				gtk_object_set_data_full(
					GTK_OBJECT(menu_item), GUI_MENU_ITEM_ACTIVATE_DATA_KEY,
					d, GUIMenuItemActivateDataDestroyCB
				);
				gtk_signal_connect_object(
					GTK_OBJECT(menu_item), "activate",
					GTK_SIGNAL_FUNC(GUIMenuItemActivateCB),
					(GtkObject *)d
				);
			}
		}

		/* GtkAlignment for the accelkey GtkLabel */
		w = gtk_alignment_new(1.0f, 0.5f, 0.0f, 0.0f);
		gtk_table_attach(
			GTK_TABLE(parent),
			w,
			attach_x, attach_x + 1,
			attach_y, attach_y + 1,
			GTK_SHRINK,
			GTK_EXPAND | GTK_FILL,
			0, 0
		);
		attach_x++;
		gtk_widget_show(w);
		parent2 = w;
		/* Create the accelkey GtkLabel */
		mi->accel_label = w = gtk_label_new("");
		gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT);
		gtk_container_add(GTK_CONTAINER(parent2), w);
		/* Set the accelkey GtkLabel */
		GUIMenuItemSetAccelKey(
			menu_item,
			accelgrp,
			accel_key, accel_mods
		);
		break;

	  case GUI_MENU_ITEM_TYPE_CHECK:
		/* Create the GtkCheckMenuItem */
		mi->menu_item = menu_item = parent = w = gtk_check_menu_item_new();
		gtk_widget_add_events(
			w,
			GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
		);
		gtk_menu_append(GTK_MENU(menu), w);
		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_MENU_ITEM_DATA_KEY,
			mi, GUIMenuItemDataDestroyCB
		);
		gtk_widget_show(w);
		/* GtkTable for the icon GtkPixmap, main GtkLabel, and
		 * accelkey GtkLabel
		 */
		ncolumns = 2;
		if(icon != NULL)
			ncolumns++;
		nrows = 1;
		w = gtk_table_new(
			nrows, ncolumns,
			FALSE
		);
		attach_x = 0;
		attach_y = 0;
		gtk_container_add(GTK_CONTAINER(parent), w);
#if !defined(_WIN32)
		gtk_table_set_col_spacing(
			GTK_TABLE(w),
			0,
			(guint)border_minor
		);
		gtk_table_set_col_spacing(
			GTK_TABLE(w),
			ncolumns - 2,
			(guint)border_accel_label
		);
#endif
		gtk_widget_show(w);
		parent = w;

		/* Create the icon GtkPixmap only if an icon was specified
		 * for GtkCheckMenuItems
		 */
		if(icon != NULL)
		{
			mi->icon_data = (const guint8 **)icon;
			mi->pixmap = w = GUICreateMenuItemIcon(icon);
			if(w != NULL)
			{
				gtk_table_attach(
					GTK_TABLE(parent),
					w,
					attach_x, attach_x + 1,
					attach_y, attach_y + 1,
					0,
					GTK_EXPAND | GTK_FILL,
					0, 0
				);
				attach_x++;
				gtk_widget_show(w);
			}
		}
		else
		{
			gtk_widget_set_usize(
				w,
				-1,
				GUI_MENU_ITEM_DEF_HEIGHT + 2
			);
		}

		/* GtkAlignment for the main GtkLabel */
		w = gtk_alignment_new(0.0f, 0.5f, 0.0f, 0.0f);
		gtk_table_attach(
			GTK_TABLE(parent),
			w,
			attach_x, attach_x + 1,
			attach_y, attach_y + 1,
			GTK_SHRINK | GTK_EXPAND | GTK_FILL,
			GTK_EXPAND | GTK_FILL,
			0, 0
		);
		attach_x++;
		gtk_widget_show(w);
		parent2 = w;
		/* Create the main GtkLabel */
		mi->label_text = g_strdup((label != NULL) ? label : "");
		mi->label = w = gtk_label_new(mi->label_text);
		gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
		gtk_container_add(GTK_CONTAINER(parent2), w);
		gtk_widget_show(w);

		/* Set the callback data and signals for this GtkCheckMenuItem */
		if(func_cb != NULL)
		{
			GUIMenuItemActivateData *d = GUI_MENU_ITEM_ACTIVATE_DATA(g_malloc0(
				sizeof(GUIMenuItemActivateData)
			));
			if(d != NULL)
			{
				d->w = menu_item;
				d->cb = func_cb;
				d->data = data;
				gtk_object_set_data_full(
					GTK_OBJECT(menu_item), GUI_MENU_ITEM_ACTIVATE_DATA_KEY,
					d, GUIMenuItemActivateDataDestroyCB
				);
				gtk_signal_connect_object(
					GTK_OBJECT(menu_item), "toggled",
					GTK_SIGNAL_FUNC(GUIMenuItemActivateCB),
					(GtkObject *)d
				);
			}
		}

		/* GtkAlignment for the accelkey GtkLabel */
		w = gtk_alignment_new(1.0f, 0.5f, 0.0f, 0.0f);
		gtk_table_attach(
			GTK_TABLE(parent),
			w,
			attach_x, attach_x + 1,
			attach_y, attach_y + 1,
			GTK_SHRINK,
			GTK_EXPAND | GTK_FILL,
			0, 0
		);
		attach_x++;
		gtk_widget_show(w);
		parent2 = w;
		/* Create the accelkey GtkLabel */
		mi->accel_label = w = gtk_label_new("");
		gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT);
		gtk_container_add(GTK_CONTAINER(parent2), w);
		/* Set the accelkey GtkLabel */
		GUIMenuItemSetAccelKey(
			menu_item,
			accelgrp,
			accel_key, accel_mods
		);
		break;

	  case GUI_MENU_ITEM_TYPE_SEPARATOR:
		/* Create a GtkMenuItem */
		mi->menu_item = menu_item = parent = w = gtk_menu_item_new();
		gtk_menu_append(GTK_MENU(menu), w);
		gtk_object_set_data_full(
			GTK_OBJECT(w), GUI_MENU_ITEM_DATA_KEY,
			mi, GUIMenuItemDataDestroyCB
		);
		GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(w), GTK_SENSITIVE);
		gtk_widget_show(w);

 	    /* Create a GtkHSeparator */
		w = gtk_hseparator_new();
		gtk_container_add(GTK_CONTAINER(parent), w);
		gtk_widget_show(w);

		break;

	  default:
		GUIMenuItemDataDestroyCB(mi);
		mi = NULL;
		break;
	}


	return(menu_item);
}

/*
 *	Sets the Menu Item as the default item.
 */
void GUISetMenuItemSetDefault(
	GtkWidget *w,
	const gboolean is_default
)
{
#if 0
	GtkRcStyle *rcstyle;
#endif
	GUIMenuItemData *d;

	if(w == NULL)
		return;

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return;

#if 0
	rcstyle = gtk_rc_style_new();
	rcstyle->font_name = STRDUP(
		"-*-*-bold-*-*-*-*-*-*-*-*-*-*-*"
/* "-adobe-helvetica-bold-r-normal-*-12-*-*-*-*-*-iso8859-1" */
	);
	w = d->label;
	if(w != NULL)
		gtk_widget_modify_style(w, rcstyle);
	w = d->accel_label;
	if(w != NULL)
		gtk_widget_modify_style(w, rcstyle);
	GTK_RC_STYLE_UNREF(rcstyle);
#endif
}

/*
 *	Sets the Menu Item's "enter_notify_event" and
 *	"leave_notify_event" signal callbacks.
 */
void GUISetMenuItemCrossingCB(
	GtkWidget *w,
	gint (*enter_cb)(GtkWidget *, GdkEvent *, gpointer),
	gpointer enter_data,
	gint (*leave_cb)(GtkWidget *, GdkEvent *, gpointer),
	gpointer leave_data
)
{
	if(w == NULL)
		return;

	if(enter_cb != NULL)
		gtk_signal_connect(
			GTK_OBJECT(w), "enter_notify_event",
			GTK_SIGNAL_FUNC(enter_cb), enter_data
		);
	if(leave_cb != NULL)
		gtk_signal_connect(
			GTK_OBJECT(w), "leave_notify_event",
			GTK_SIGNAL_FUNC(leave_cb), leave_data
		);
}

/*
 *	Adds the Menu to the Menu Bar with the specified label.
 *
 *	Returns the Menu Bar Label or NULL on error.
 */
GtkWidget *GUIMenuAddToMenuBar(
	GtkWidget *menu_bar,
	GtkWidget *menu,
	const gchar *label,
	const GtkJustification justify
)
{
	GtkWidget *menu_bar_item;

	if((menu_bar == NULL) || (menu == NULL))
		return(NULL);

	/* Create the menu bar item */
	if(label != NULL)
	{
		gtk_menu_set_title(GTK_MENU(menu), label);
		menu_bar_item = gtk_menu_item_new_with_label(label);
	}
	else
	{
		GtkMenuShell *menu_shell = GTK_MENU_SHELL(menu_bar);
		GList *glist = (menu_shell != NULL) ?
			menu_shell->children : NULL;
		gchar *s = g_strdup_printf(
			"Menu %i",
			g_list_length(glist) + 1
		);
		gtk_menu_set_title(GTK_MENU(menu), s);
		menu_bar_item = gtk_menu_item_new_with_label(s);
		g_free(s);
	}

	/* Set the new menu bar item to refer to the menu */
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_bar_item), menu);

	/* Append the new menu bar item to the menu bar */
	gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_bar_item);

	gtk_widget_show(menu_bar_item);

	/* Set the menu bar item's justification */
	switch(justify)
	{
	  case GTK_JUSTIFY_LEFT:
		break;
	  case GTK_JUSTIFY_RIGHT:
		gtk_menu_item_right_justify(GTK_MENU_ITEM(menu_bar_item));
		break;
	  case GTK_JUSTIFY_CENTER:
	  case GTK_JUSTIFY_FILL:
		break;
	}

	return(menu_bar_item);
}

/*
 *	Sets the Menu Item's label.
 */
void GUIMenuItemSetLabel(
	GtkWidget *menu_item,
	const gchar *label
)
{
	GtkWidget *w;
	GUIMenuItemData *d;

	if(menu_item == NULL)
		return;

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(menu_item),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return;

	w = d->label;
	if(w == NULL)
		return;

	if(label != NULL)
	{
		/* No change in label text? */
		const gchar *old_label = d->label_text;
		if(old_label != NULL)
		{
			if(!strcmp(old_label, label))
				return;
		}

		/* Record the new label text */
		g_free(d->label_text);
		d->label_text = g_strdup(label);

		/* Set the new label text */
		gtk_label_set_text(
			GTK_LABEL(w),
			d->label_text
		);
	}
	else
	{
		/* Label text was already empty? */
		if(d->label_text == NULL)
			return;

		/* Record the new label text */
		g_free(d->label_text);
		d->label_text = g_strdup("(null)");
    
		/* Set the new label text */
		gtk_label_set_text(
			GTK_LABEL(w),
			d->label_text
		);
	}
}

/*
 *	Gets the GtkMenuItem's main GtkLabel.
 */
GtkWidget *GUIMenuItemGetLabelWidget(GtkWidget *menu_item)
{
	GUIMenuItemData *d;

	if(menu_item == NULL)
		return(NULL);

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(menu_item),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return(NULL);

	return(d->label);
}

/*
 *	Sets the Menu Item's pixmap.
 */
void GUIMenuItemSetPixmap(
	GtkWidget *menu_item,
	guint8 **icon_data
)
{
	GdkBitmap *mask;
	GdkPixmap *pixmap;
	GtkWidget *w;
	GUIMenuItemData *d;

	if((menu_item == NULL) || (icon_data == NULL))
		return;

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(menu_item),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return;

	w = d->pixmap;
	if(w == NULL)
		return;

	/* No change in icon data? */
	if(d->icon_data == (const guint8 **)icon_data)
		return;

	/* Record the new icon data */
	d->icon_data = (const guint8 **)icon_data;

	pixmap = GUIPixmapNewFromXPMData(
		w->window,
		&mask,
		NULL,
		icon_data
	);
	if(pixmap != NULL)
	{
		/* Update the pixmap */
		gtk_pixmap_set(
			GTK_PIXMAP(w),
			pixmap, mask
		);

		GDK_PIXMAP_UNREF(pixmap);
		GDK_BITMAP_UNREF(mask);
	}
}

/*
 *	Sets the Menu Item's pixmap.
 */
void GUIMenuItemSetPixmap2(
	GtkWidget *menu_item, 
	GdkPixmap *pixmap, GdkBitmap *mask
)
{
	GtkWidget *w;
	GUIMenuItemData *d;

	if(menu_item == NULL)
		return;

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(menu_item),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return;

	w = d->pixmap;
	if(w == NULL)
		return;

	/* Clear the recorded icon data */
	d->icon_data = NULL;

	/* Update the pixmap */
	gtk_pixmap_set(
		GTK_PIXMAP(w),
		pixmap, mask
	);
}

/*
 *	Sets the Menu Item's accelerator key.
 *
 *	If accelgrp is NULL then only the accelerator key label will be
 *	updated.
 */
void GUIMenuItemSetAccelKey(
	GtkWidget *menu_item,
	GtkAccelGroup *accelgrp,
	const guint accel_key, const guint accel_mods
)
{
	GtkWidget *w;
	GUIMenuItemData *d;

	if(menu_item == NULL)
		return;

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(menu_item),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return;

	/* No change? */
	if((d->accel_key == accel_key) &&
	   (d->accel_mods == accel_mods)
	)
		return;
 
	/* Update the GtkAccelGroup? */
	if(accelgrp != NULL)
	{
		/* Remove the existing accelerator key? */
		if(d->accel_key != 0x00000000)
			gtk_widget_remove_accelerator(
				menu_item,
				accelgrp,
				d->accel_key, d->accel_mods
			);

		/* Add the new accelerator key */
		gtk_widget_add_accelerator(
			menu_item,
			"activate",
			accelgrp,
			accel_key, accel_mods,
			GTK_ACCEL_VISIBLE
		);
	}

	/* Update the accelerator key GtkLabel */
	w = d->accel_label;
	if(w != NULL)
	{
		GtkLabel *label = GTK_LABEL(w);

		/* Accelerator key specified? */
		if(accel_key != 0x00000000)
		{
			const gchar *key_name;
			gchar *accel_text = g_strdup("");

			/* Append modifier name to accelerator text */
			if(accel_mods & GDK_LOCK_MASK)
			{
				gchar *s = g_strconcat(accel_text, "CapsLock+", NULL);
				g_free(accel_text);
				accel_text = s;
			}
			if(accel_mods & GDK_CONTROL_MASK)
			{
				gchar *s = g_strconcat(accel_text, "Ctrl+", NULL);
				g_free(accel_text);
				accel_text = s;
			} 
			if(accel_mods & GDK_SHIFT_MASK)
			{
				gchar *s = g_strconcat(accel_text, "Shift+", NULL);
				g_free(accel_text);
				accel_text = s;
			}
			if(accel_mods & GDK_MOD1_MASK)
			{
				gchar *s = g_strconcat(accel_text, "Alt+", NULL);
				g_free(accel_text);
				accel_text = s;
			}
			if(accel_mods & GDK_MOD2_MASK)
			{
				gchar *s = g_strconcat(accel_text, "NumLock+", NULL);
				g_free(accel_text);
				accel_text = s;
			}
			if(accel_mods & GDK_MOD3_MASK)
			{
				gchar *s = g_strconcat(accel_text, "Mod3+", NULL);
				g_free(accel_text);
				accel_text = s;
			}
			if(accel_mods & GDK_MOD4_MASK)
			{
				gchar *s = g_strconcat(accel_text, "Mod4+", NULL);
				g_free(accel_text);
				accel_text = s;
			}
			if(accel_mods & GDK_MOD5_MASK)
			{
				gchar *s = g_strconcat(accel_text, "ScrollLock+", NULL);
				g_free(accel_text);
				accel_text = s;
			}

			/* Append key name to accelerator key text */
			key_name = GUIGetKeyName(accel_key);
/*		key_name = gdk_keyval_name(accel_key); */
			if(*key_name != '\0')
			{
				gchar *s = g_strconcat(accel_text, key_name, NULL);
				g_free(accel_text);
				accel_text = s;
			}
			else
			{
				gchar *char_name = g_strdup_printf(
					"%c",
					toupper(accel_key)
				);
				gchar *s = g_strconcat(
					accel_text,
					char_name,
					NULL
				);
				g_free(char_name);
				g_free(accel_text);
				accel_text = s;
			}

			/* Set and map accelerator key GtkLabel */
			g_free(d->accel_text);
			d->accel_text = STRDUP(accel_text);
			gtk_label_set_text(label, accel_text);
			gtk_widget_show(w);

			g_free(accel_text);
		}
		else
		{
			/* Clear and unmap accelerator key GtkLabel */
			g_free(d->accel_text);
			d->accel_text = NULL;
			gtk_label_set_text(label, "");
			gtk_widget_hide(w);
		}
	}

	/* Record new accelerator key value */
	d->accel_key = accel_key;
	d->accel_mods = accel_mods;
}

/*
 *	Sets the Check Menu Item's active state.
 */
void GUIMenuItemSetCheck(
	GtkWidget *menu_item,
	const gboolean active,
	const gboolean emit_signal
)
{
	GtkWidget *w;
	GUIMenuItemData *d;

	if(menu_item == NULL)
		return;

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(menu_item),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return;

	w = d->menu_item;
	if(w == NULL)
		return;

	if(!GTK_IS_CHECK_MENU_ITEM(w))
		return;

	if(emit_signal)
		gtk_check_menu_item_set_active(
			GTK_CHECK_MENU_ITEM(w),
			active
		);
	else
		GTK_CHECK_MENU_ITEM(w)->active = active;
}

/*
 *	Gets the Check Menu Item's active state.
 */
gboolean GUIMenuItemGetCheck(GtkWidget *menu_item)
{
	GtkWidget *w;
	GUIMenuItemData *d;

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

	d = GUI_MENU_ITEM_DATA(gtk_object_get_data(
		GTK_OBJECT(menu_item),
		GUI_MENU_ITEM_DATA_KEY
	));
	if(d == NULL)
		return(FALSE);

	w = d->menu_item;
	if(w == NULL)
		return(FALSE);

	if(GTK_IS_CHECK_MENU_ITEM(w))
		return(GTK_CHECK_MENU_ITEM(w)->active);
	else
		return(FALSE);
}


/*
 *	Same as GUIMenuAddToMenuBar() except a pixmap is placed to the
 *	left of the label on the menu bar.
 */
GtkWidget *GUIMenuAddToMenuBarPixmapH(
	GtkWidget *menu_bar,
	GtkWidget *menu,
	const gchar *label,
	guint8 **icon_data,
	const GtkJustification justify
)
{
	return(GUIMenuAddToMenuBar(
		menu_bar,
		menu,
		label,
		justify
	));
}

/*
 *	Sets the menu item's sub menu.
 *
 *	If the menu item's previous submenu exists then it will be
 *	destroyed first.
 *
 *	If sub_menu is NULL then the menu item's previous submenu (if
 *	any) be destroyed and the menu item will be set as not having
 *	any submenu.
 */
void GUIMenuItemSetSubMenu(
	GtkWidget *menu_item,
	GtkWidget *sub_menu
)
{
	GtkMenuItem *mi;

	if((menu_item != NULL) ? !GTK_IS_MENU_ITEM(menu_item) : TRUE)
		return;

	mi = GTK_MENU_ITEM(menu_item);

	if((sub_menu != NULL) ? GTK_IS_MENU(sub_menu) : FALSE)
		gtk_menu_item_set_submenu(mi, sub_menu);
	else
		gtk_menu_item_remove_submenu(mi);
}


/*
 *	Editable popup menu record undo.
 */
static void GUIEditablePopupMenuRecordUndo(GUIEditablePopupMenuData *d)
{
	GtkEditable *editable = GTK_EDITABLE(d->editable);

	g_free(d->undo_buf);
	d->undo_buf = gtk_editable_get_chars(editable, 0, -1);
	d->undo_last_pos = gtk_editable_get_position(editable);

	g_free(d->redo_buf);
	d->redo_buf = NULL;
	d->redo_last_pos = 0;
}

/*
 *	Editable popup menu update.
 */
static void GUIEditablePopupMenuUpdate(GUIEditablePopupMenuData *d)
{
	GtkEditable *editable = GTK_EDITABLE(d->editable);
	const gboolean has_selection = editable->has_selection;
	gboolean b;

	d->freeze_count++;

	if(d->flags & GUI_EDITABLE_POPUP_MENU_UNDO)
	{
		b = ((d->undo_buf != NULL) || (d->redo_buf != NULL)) ?
			TRUE : FALSE;
		GTK_WIDGET_SET_SENSITIVE(d->undo_mi, b);
		GUIMenuItemSetLabel(
			d->undo_mi,
			(d->redo_buf != NULL) ? "Redo" : "Undo"
		);
		gtk_widget_show(d->undo_mi);
		gtk_widget_show(d->undo_msep);
	}
	else
	{
		gtk_widget_hide(d->undo_mi);
		gtk_widget_hide(d->undo_msep);
	}

	if(d->flags & GUI_EDITABLE_POPUP_MENU_READ_ONLY)
	{
		gtk_widget_hide(d->cut_mi);
		gtk_widget_show(d->copy_mi);
		gtk_widget_hide(d->paste_mi);
		gtk_widget_hide(d->delete_mi);
		gtk_widget_show(d->select_msep);
		gtk_widget_show(d->select_all_mi);
		gtk_widget_show(d->unselect_all_mi);
	}
	else
	{
		gtk_widget_show(d->cut_mi);
		gtk_widget_show(d->copy_mi);
		gtk_widget_show(d->paste_mi);
		gtk_widget_show(d->delete_mi);
		gtk_widget_show(d->select_msep);
		gtk_widget_show(d->select_all_mi);
		gtk_widget_show(d->unselect_all_mi);
	}

	GTK_WIDGET_SET_SENSITIVE(d->cut_mi, has_selection);
	GTK_WIDGET_SET_SENSITIVE(d->copy_mi, has_selection);
	GTK_WIDGET_SET_SENSITIVE(d->paste_mi, TRUE);
	GTK_WIDGET_SET_SENSITIVE(d->delete_mi, has_selection);
	GTK_WIDGET_SET_SENSITIVE(d->select_all_mi, TRUE);
	GTK_WIDGET_SET_SENSITIVE(d->unselect_all_mi, TRUE);

	d->freeze_count--;
}

/*
 *	Gets the popup menu from the GtkEditable.
 */
GtkWidget *GUIEditableGetPopupMenu(GtkWidget *w)
{
	GUIEditablePopupMenuData *d;

	if(w == NULL)
		return(NULL);

	d = GUI_EDITABLE_POPUP_MENU_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_EDITABLE_POPUP_MENU_DATA_KEY
	));
	if(d == NULL)
		return(NULL);

	return(d->menu);
}

/*
 *	Endows the GtkEditable with a popup menu for editing.
 *
 *	The w specifies the GtkEditable.
 *
 *	The flags specifies the popup menu options.
 */
void GUIEditableEndowPopupMenu(
	GtkWidget *w,
	const gui_editable_popup_menu_flags flags
)
{
	guint8 **icon;
	const gchar *label;
	guint accel_key, accel_mods;
	gpointer menu_item_data;
	GtkAccelGroup *accelgrp;
	GtkWidget *menu, *menu_item;
	GUIEditablePopupMenuData *d;
	void (*func_cb)(GtkWidget *w, gpointer);

	if(w == NULL)
		return;

	if(!GTK_IS_EDITABLE(w))
		return;

	/* Already set? */
	d = GUI_EDITABLE_POPUP_MENU_DATA(gtk_object_get_data(
		GTK_OBJECT(w),
		GUI_EDITABLE_POPUP_MENU_DATA_KEY
	));
	if(d != NULL)
		return;

	/* Allocate the editable popup menu data */
	d = GUI_EDITABLE_POPUP_MENU_DATA(g_malloc0(
		sizeof(GUIEditablePopupMenuData)
	));
	if(d == NULL)
		return;

	d->editable = w;
	d->freeze_count = 0;
	d->flags = flags;
	d->undo_buf = NULL;
	d->redo_buf = NULL;

	d->freeze_count++;

	gtk_object_set_data_full(
		GTK_OBJECT(w), GUI_EDITABLE_POPUP_MENU_DATA_KEY,
		d, GUIEditablePopupMenuDataDestroyCB
	);

	/* Right-click popup menu */
	d->menu = menu = GUIMenuCreate();
	accelgrp = NULL;
	accel_key = 0;
	accel_mods = 0;
	menu_item_data = d;
	func_cb = NULL;

#define ADD_MENU_ITEM_LABEL	{		\
 menu_item = GUIMenuItemCreate(			\
  menu,						\
  GUI_MENU_ITEM_TYPE_LABEL,			\
  accelgrp,					\
  icon, label,					\
  accel_key, accel_mods,			\
  func_cb, menu_item_data			\
 );						\
}
#define ADD_MENU_ITEM_CHECK	{		\
 menu_item = GUIMenuItemCreate(			\
  menu,						\
  GUI_MENU_ITEM_TYPE_CHECK,			\
  accelgrp,					\
  icon, label,					\
  accel_key, accel_mods,			\
  func_cb, menu_item_data			\
 );						\
}
#define ADD_MENU_ITEM_SEPARATOR	{		\
 menu_item = GUIMenuItemCreate(			\
  menu,						\
  GUI_MENU_ITEM_TYPE_SEPARATOR,			\
  NULL,						\
  NULL, NULL,					\
  0, 0,						\
  NULL, NULL					\
 );						\
}

	icon = NULL;
	label = "Undo";
	func_cb = GUIEditablePopupMenuUndoCB;
	ADD_MENU_ITEM_LABEL
	d->undo_mi = menu_item;

	ADD_MENU_ITEM_SEPARATOR
	d->undo_msep = menu_item;

	icon = (guint8 **)icon_cut_20x20_xpm;
	label =
#if defined(PROG_LANGUAGE_SPANISH)
"Corte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Couper"
#elif defined(PROG_LANGUAGE_GERMAN)
"Schnitt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Taglio"
#elif defined(PROG_LANGUAGE_DUTCH)
"Snee"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Corte"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Snitt"
#else
"Cut"
#endif
	;
	func_cb = GUIEditablePopupMenuCutCB;
	ADD_MENU_ITEM_LABEL
	d->cut_mi = menu_item;
						  
	icon = (guint8 **)icon_copy_20x20_xpm;
	label =
#if defined(PROG_LANGUAGE_SPANISH)
"Copia"
#elif defined(PROG_LANGUAGE_FRENCH)
"Copier"
#elif defined(PROG_LANGUAGE_GERMAN)
"Kopie"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Copia"
#elif defined(PROG_LANGUAGE_DUTCH)
"Kopie"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Cópia"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kopi"
#else
"Copy"
#endif
	;
	func_cb = GUIEditablePopupMenuCopyCB;
	ADD_MENU_ITEM_LABEL
	d->copy_mi = menu_item;

	icon = (guint8 **)icon_paste_20x20_xpm;
	label =
#if defined(PROG_LANGUAGE_SPANISH)
"Pasta"
#elif defined(PROG_LANGUAGE_FRENCH)
"Coller"
#elif defined(PROG_LANGUAGE_GERMAN)
"Paste"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Pasta"
#elif defined(PROG_LANGUAGE_DUTCH)
"Plakmiddel"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Pasta"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Masse"
#else
"Paste"
#endif
	;
	func_cb = GUIEditablePopupMenuPasteCB;
	ADD_MENU_ITEM_LABEL
	d->paste_mi = menu_item;

	icon = (guint8 **)icon_cancel_20x20_xpm;
	label =
#if defined(PROG_LANGUAGE_SPANISH)
"Borre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Supprimer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk"
#else
"Delete"
#endif
	;
	func_cb = GUIEditablePopupMenuDeleteCB;
	ADD_MENU_ITEM_LABEL
	d->delete_mi = menu_item;

	ADD_MENU_ITEM_SEPARATOR
	d->select_msep = menu_item;

	icon = NULL;
	label = "Select All";
	func_cb = GUIEditablePopupMenuSelectAllCB;
	ADD_MENU_ITEM_LABEL
	d->select_all_mi = menu_item;

	icon = NULL;
	label = "Unselect All";
	func_cb = GUIEditablePopupMenuUnselectAllCB;
	ADD_MENU_ITEM_LABEL
	d->unselect_all_mi = menu_item;

#undef ADD_MENU_ITEM_LABEL
#undef ADD_MENU_ITEM_CHECK
#undef ADD_MENU_ITEM_SEPARATOR

	/* Attach signals to trigger the mapping of the popup menu
	 * and to monitor change
	 */
	gtk_signal_connect(
		GTK_OBJECT(w), "key_press_event",
		GTK_SIGNAL_FUNC(GUIEditablePopupMenuEditableEventCB), d
	);
	gtk_signal_connect(
		GTK_OBJECT(w), "key_release_event",
		GTK_SIGNAL_FUNC(GUIEditablePopupMenuEditableEventCB), d
	);
	gtk_signal_connect(
		GTK_OBJECT(w), "button_press_event",
		GTK_SIGNAL_FUNC(GUIEditablePopupMenuEditableEventCB), d
	);
	gtk_signal_connect(
		GTK_OBJECT(w), "button_release_event",
		GTK_SIGNAL_FUNC(GUIEditablePopupMenuEditableEventCB), d
	);

	GUIEditablePopupMenuUpdate(d);

	d->freeze_count--;
}


/*
 *	Creates a pull out widget horizontally, returning the hbox which
 *	the calling function can pack child widgets into.
 *
 *	The given parent must be a hbox or a vbox.
 *
 *	The toplevel_width and toplevel_height specify the size of the
 *	toplevel window that will be used when this widget is `pulled
 *	out'.
 *
 *	gtk_widget_show() will be called for you, the client function need
 *	not call it.
 */
void *GUIPullOutCreateH(
	void *parent_box,
	int homogeneous, int spacing,		/* Of client vbox */
	int expand, int fill, int padding,	/* Of holder hbox */
	int toplevel_width, int toplevel_height,
	void *pull_out_client_data,
	void (*pull_out_cb)(void *, void *),
	void *push_in_client_data,
	void (*push_in_cb)(void *, void *)
)
{
	gpointer *cb_data;
	GtkWidget *pull_out_btn, *holder_hbox, *client_hbox;

	if(parent_box == NULL)
		return(NULL);

	/* Create a hbox to place into the given parent box. This hbox
	 * will hold the parenting hbox plus some other widgets.
	 */
	holder_hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(
		(GtkBox *)parent_box, holder_hbox,
		expand, fill, padding
	);
	gtk_widget_show(holder_hbox);

	/* Create pull out button and parent it to the holder hbox.
	 * Note that this is not really a button but an event box.
	 */
	pull_out_btn = gtk_event_box_new();
	gtk_box_pack_start(
		GTK_BOX(holder_hbox), pull_out_btn, FALSE, FALSE, 0
	);
	gtk_widget_set_usize(pull_out_btn, 10, -1);
	gtk_widget_show(pull_out_btn);


	/* Create the client hbox, which will be parented to the above
	 * holder hbox when `placed in' and reparented to a toplevel
	 * GtkWindow when `pulled out'.
	 */
	client_hbox = gtk_hbox_new(homogeneous, spacing);
	gtk_box_pack_start(
		GTK_BOX(holder_hbox), client_hbox, TRUE, TRUE, 0
	);
	gtk_widget_show(client_hbox);


	/* Set callbacks and callback data */

	/* Allocate and set up callback data */
	cb_data = (gpointer *)g_malloc0(13 * sizeof(gpointer));
	if(cb_data != NULL)
	{
		/* Format is as follows (13 arguments):
		 *
		 *	client_hbox
		 *	holder_hbox
		 *	holder_window
		 *	holder_window_x
		 *	holder_window_y
		 *	holder_window_width
		 *	holder_window_height
		 *	in_place		(1 if true).
		 *	pull_out_client_data
		 *	pull_out_cb
		 *	push_in_client_data
		 *	push_in_cb
		 */
		cb_data[0] = (gpointer)client_hbox;
		cb_data[1] = (gpointer)holder_hbox;
		cb_data[2] = NULL;
		cb_data[3] = 0;
		cb_data[4] = 0;
		cb_data[5] = (gpointer)MAX(toplevel_width, 0);
		cb_data[6] = (gpointer)MAX(toplevel_height, 0);
		cb_data[7] = (gpointer)1;		/* Initially `pushed in' */
		cb_data[8] = (gpointer)pull_out_client_data;
		cb_data[9] = (gpointer)pull_out_cb;
		cb_data[10] = (gpointer)push_in_client_data;
		cb_data[11] = (gpointer)push_in_cb;
		cb_data[12] = NULL;
		gtk_object_set_data_full(
			GTK_OBJECT(client_hbox), GUI_PULLOUT_DATA_KEY,
			cb_data, GUIPullOutDataDestroyCB
		);
	}

	gtk_signal_connect(
		GTK_OBJECT(pull_out_btn), "button_press_event",
		GTK_SIGNAL_FUNC(GUIPullOutPullOutBtnCB), cb_data
	);
	gtk_signal_connect_after(
		GTK_OBJECT(pull_out_btn), "expose_event",
		GTK_SIGNAL_FUNC(GUIPullOutDrawHandleCB), NULL
	);
	gtk_signal_connect(
		GTK_OBJECT(pull_out_btn), "draw",
		GTK_SIGNAL_FUNC(GUIPullOutDrawHandleDrawCB), NULL
	);

	return(client_hbox);
}

/*
 *	Returns the pointer to the toplevel window (if any) of the
 *	client_hbox and the geometry of that window.
 *
 *	The client box should be one created by GUIPullOutCreate*().
 */
void *GUIPullOutGetToplevelWindow(
	void *client_box,
	int *x, int *y, int *width, int *height
)
{
	gpointer *cb_data;
	GtkWidget *w = NULL;

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

	if(client_box == NULL)
		return(w);

	cb_data = (gpointer *)gtk_object_get_data(
		GTK_OBJECT(client_box),
		GUI_PULLOUT_DATA_KEY
	);
	if(cb_data != NULL)
	{
		/* Format is as follows (13 arguments):
		 *
		 *	client_hbox
		 *	holder_hbox
		 *	holder_window
		 *	holder_window_x
		 *	holder_window_y
		 *	holder_window_width
		 *	holder_window_height
		 *	in_place		(1 if true)
		 *	pull_out_client_data
		 *	pull_out_cb
		 *	push_in_client_data
		 *	push_in_cb   
		 */
		w = (GtkWidget *)cb_data[2];

		if((w != NULL) ? !GTK_WIDGET_NO_WINDOW(w) : FALSE)
		{
			GdkWindow *window = w->window;
			gint rx, ry, rwidth, rheight, rdepth;

			gdk_window_get_geometry(
				window,
				&rx, &ry, &rwidth, &rheight,
				&rdepth
			);

			if(x != NULL)
				*x = rx;
			if(y != NULL)
				*y = ry;
			if(width != NULL)
				*width = rwidth;
			if(height != NULL)
				*height = rheight;
		}
	}

	return(w);
}

/*
 *	Pulls out the pull out box.
 */
void GUIPullOutPullOut(void *client_box)
{
	gpointer *cb_data;

	if(client_box == NULL)
		return;

	cb_data = (gpointer *)gtk_object_get_data(
		GTK_OBJECT(client_box),
		GUI_PULLOUT_DATA_KEY
	);
	if(cb_data != NULL)
	{
		/* Format is as follows (13 arguments):
		 *
		 *	client_hbox
		 *	holder_hbox
		 *	holder_window
		 *	holder_window_x
		 *	holder_window_y
		 *	holder_window_width
		 *	holder_window_height
		 *	in_place		(1 if true)
		 *	pull_out_client_data
		 *	pull_out_cb
		 *	push_in_client_data
		 *	push_in_cb
		 */

		/* In place (pushed in)? */
		if((gint)cb_data[7])
		{
			GUIPullOutPullOutBtnCB(
				NULL,
				NULL,
				(gpointer)cb_data
			);
		}
	}
}

/*
 *	Pushes in the pull out box.
 */
void GUIPullOutPushIn(void *client_box)
{
	GtkWidget *w;
	gpointer *cb_data;

	if(client_box == NULL)
		return;

	cb_data = (gpointer *)gtk_object_get_data(
		GTK_OBJECT(client_box),
		GUI_PULLOUT_DATA_KEY
	);
	if(cb_data != NULL)
	{
		/* Format is as follows (13 arguments):
		 *
		 *	client_hbox
		 *	holder_hbox
		 *	holder_window
		 *	holder_window_x
		 *	holder_window_y
		 *	holder_window_width
		 *	holder_window_height
		 *	in_place		(1 if true)
		 *	pull_out_client_data
		 *	pull_out_cb
		 *	push_in_client_data
		 *	push_in_cb
		 */

		w = (GtkWidget *)cb_data[2];

		/* Not in place (pulled out)? */
		if(!((gint)cb_data[7]))
		{
			GUIPullOutCloseCB(
				(GtkWidget *)cb_data[2],
				NULL,
				(gpointer)cb_data
			);
		}
	}
}
