Logo Search packages:      
Sourcecode: libjsw version File versions  Download package

guiutils.c

#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/gdkwin32.h>
# define HAVE_WIN32
#else
# include <gdk/gdkx.h>
# define HAVE_X
#endif

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

#include "guiutils.h"


#include "images/icon_browse_20x20.xpm"


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

/*
 *    Default GTK Style:
 */
static GtkStyle         *gui_style_default = NULL;


/*
 *    GtkInvisible Toplevel Widget:
 *
 *    For grabbing and blocking input in GUIBlockInput().
 */
static GtkWidget  *gui_invisible_blocker_widget = NULL;
static gint       gui_block_input_level = 0;


/*
 *    DDE Owner Callback Data:
 */
typedef struct {

      GtkWidget   *w;               /* Owner GtkWidget */
      GdkAtom           target_atom;            /* GdkTarget */
      GdkAtom           type_atom;        /* GdkSelectionType */
      guint       selection_clear_event_id,
                  selection_get_id;

      gpointer    data;             /* Data (coppied) */
      gint        length;                 /* Data length in bytes */

} gui_dde_owner_struct;
#define GUI_DDE_OWNER(p)      ((gui_dde_owner_struct *)(p))
#define GUI_DDE_OWNER_DATA_KEY      "gui_dde_owner"

/*
 *    DND Callback Data:
 *
 *    Used for callback data in GUIDND*CB() functions.
 */
typedef struct {
      GdkDragAction     default_action_same,    /* Action to use if same widget */
                  default_action;         /* Action for all other cases */
} gui_dnd_cb_struct;
#define GUI_DND_CB(p)   ((gui_dnd_cb_struct *)(p))

/*
 *    DND Icon Widget:
 *
 *    Initialized on first call to GUIDNDSetDragIcon() and used in
 *    GUIDNDDragBeginCB() and GUIDNDDragReceivedCB().
 */
typedef struct {

      GtkWidget   *toplevel,
                  *icon_pm;

      gint        x,          /* Hot spot */
                  y,
                  width,            /* Size */
                  height;

} gui_dnd_icon_struct;
#define GUI_DND_ICON(p) ((gui_dnd_icon_struct *)(p))
static gui_dnd_icon_struct gui_dnd_icon;


/*
 *    Banner Data:
 */
#define GUI_BANNER_DATA_KEY   "gui_banner_data"


/*
 *    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 */
                  *pixmap,    /* GtkPixmap */
                  *arrow;           /* GtkArrow */

      gchar       *label_text;      /* Copy of label text */
      const guint8      **icon_data;      /* Shared icon data */
      GtkArrowType      arrow_type;

} gui_button_data_struct;
#define GUI_BUTTON_DATA(p)    ((gui_button_data_struct *)(p))
#define GUI_BUTTON_DATA_KEY   "gui_button_data"

/*
 *    Pullout Data:
 */
#define GUI_PULLOUT_DATA_KEY  "gui_pullout_data"

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

/*
 *    Menu Item Data:
 *
 *    For recording widgets on menu items, stored as the object's
 *    user data.
 */
typedef struct {

      GtkWidget   *menu_item,
                  *label,
                  *accel_label,
                  *pixmap;

      gchar       *label_text,      /* Copy of label text */
                  *accel_text;      /* Copy of accel label text */
      const guint8      **icon_data;      /* Shared icon data */

      guint       accel_key,
                  accel_mods;

} gui_menu_item_data_struct;
#define GUI_MENU_ITEM_DATA(p) ((gui_menu_item_data_struct *)(p))
#define GUI_MENU_ITEM_DATA_KEY      "gui_menu_item_data"
#define GUI_MENU_ITEM_CLIENT_DATA_KEY     "gui_menu_item_client_data"

/*
 *    Tool Tips 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 *GUIGetKeyName(guint key);

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

static void GUIDNDDragBeginCB(
      GtkWidget *widget, GdkDragContext *dc, gpointer data
);
static gboolean GUIDNDDragMotionCB(
      GtkWidget *widget, GdkDragContext *dc,
      gint x, gint y, guint t,
      gpointer data
);
static gboolean GUIDNDDragDropCB(
      GtkWidget *widget, GdkDragContext *dc,
      gint x, gint y, guint t,
      gpointer data
);
static void GUIDNDDragReceivedCB(
      GtkWidget *widget, GdkDragContext *dc,
      gint x, gint y,
      GtkSelectionData *selection_data, guint info, guint t,
      gpointer data
);
static void GUIDNDDragEndCB(
      GtkWidget *widget, GdkDragContext *dc,
      gpointer data
);

static void GUIButtonDataDestroyCB(gpointer data);

static gint GUIBannerEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
);
static void GUIBannerDataDestroyCB(gpointer data);

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

static void GUIMenuActivateCB(gpointer data);
static void GUIMenuClientDataDestroyCB(gpointer data);
static void GUIMenuDataDestroyCB(gpointer data);

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, gint column, gint row,
      gint *x, gint *y, gint *width, gint *height
);
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
);
void GUIGetWindowRootPosition(
      GdkWindow *window, gint *x, gint *y
);
void GUIGetPositionRoot(
      GdkWindow *w, gint x, gint y, gint *rx, gint *ry
);


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


/* GdkGC Utilities */
GdkGC *GDK_GC_NEW(void);


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


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


/* GtkWindow Utilities */
void GUIWindowApplyArgs(GtkWindow *w, gint argc, gchar **argv);


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


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


/* Pixmap Utilities */
GdkPixmap *GDK_PIXMAP_NEW(gint width, gint height);
GdkPixmap *GDK_PIXMAP_NEW_FROM_XPM_DATA(
      GdkBitmap **mask_rtn, guint8 **data
);
GdkPixmap *GDK_PIXMAP_NEW_FROM_XPM_FILE(
      GdkBitmap **mask_rtn, const gchar *filename
);
GtkWidget *gtk_pixmap_new_from_xpm_d(
      GdkWindow *window, GdkColor *transparent_color,
      guint8 **data
);
GtkWidget *GUICreateMenuItemIcon(guint8 **icon_data);


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


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


/* Buttons With Icon & Label */
void GUIButtonChangeLayout(
      GtkWidget *w, gboolean show_pixmap, gboolean show_label
);
void GUIButtonLabelUnderline(GtkWidget *w, guint c);
void GUIButtonPixmapUpdate(
      GtkWidget *w, guint8 **icon, const gchar *label
);
void GUIButtonArrowUpdate(
      GtkWidget *w,
      GtkArrowType arrow_type, gint arrow_width, gint arrow_height,
      const gchar *label
);
GtkWidget *GUIButtonGetMainBox(GtkWidget *w);
GtkWidget *GUIButtonGetLabel(GtkWidget *w);
GtkWidget *GUIButtonGetPixmap(GtkWidget *w);
GtkWidget *GUIButtonGetArrow(GtkWidget *w);
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(
      GtkArrowType arrow_type, gint arrow_width, gint arrow_height,
      const gchar *label, GtkWidget **label_rtn,
      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,
      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
);


/* Dynamic Data Exchange */
void GUIDDESetBinary(
      GtkWidget *w,                 /* Owner widget */
      GdkAtom selection_atom,       /* Can be GDK_NONE */
      gulong t,               /* Time */
      const guint8 *data, gint length
);
guint8 *GUIDDEGetBinary(
      GtkWidget *w,                 /* Widget making request */
      GdkAtom selection_atom,       /* Can be GDK_NONE */
      gulong t,               /* Time */
      gint *length
);
void GUIDDESetString(
      GtkWidget *w,                 /* Owner widget */
      GdkAtom selection_atom,       /* Can be GDK_NONE */
      gulong t,               /* Time */
      const gchar *data
);
gchar *GUIDDEGetString(
      GtkWidget *w,                 /* Widget making request */
      GdkAtom selection_atom,       /* Can be GDK_NONE */
      gulong t                /* Time */
);


/* Drag & Drop Utilities */
void GUIDNDSetSrc(
      void *w, const void *drag_type, int ndrag_types,
      unsigned int actions, unsigned int buttons,
      void (*begin_cb)(GtkWidget *, GdkDragContext *, gpointer),
      void (*request_data_cb)(GtkWidget *, GdkDragContext *,
            GtkSelectionData *, guint, guint, gpointer),
      void (*delete_data_cb)(GtkWidget *, GdkDragContext *, gpointer),
      void (*end_cb)(GtkWidget *, GdkDragContext *, gpointer),
      gpointer client_data
);
void GUIDNDSetTar(
      void *w, const void *drag_type, int ndrag_types,
      unsigned int actions, unsigned int default_action_same,
      unsigned int default_action,
      void (*recieved_data_cb)(GtkWidget *, GdkDragContext *,
            gint, gint, GtkSelectionData *, guint, guint,
            gpointer
      ),
      gpointer client_data
);
void GUIDNDSetDragIcon(
      GdkPixmap *pixmap, GdkBitmap *mask, gint hot_x, gint hot_y
);


/* Banners */
GtkWidget *GUIBannerCreate(
      const gchar *label,
      const gchar *font_name,
      GdkColor color_fg,
      GdkColor color_bg,
      GtkJustification justify,           /* One of GTK_JUSTIFY_* */
      gboolean expand
);
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);


/* Menu Bar & Menu Item */
GtkWidget *GUIMenuBarCreate(GtkAccelGroup **accelgrp_rtn);
GtkWidget *GUIMenuCreateTearOff(void);
GtkWidget *GUIMenuCreate(void);
GtkWidget *GUIMenuItemCreate(
      GtkWidget *menu,
      gui_menu_item_type type,      /* One of GUI_MENU_ITEM_TYPE_* */
      GtkAccelGroup *accelgrp,
      guint8 **icon, const gchar *label,
      guint accel_key, guint accel_mods,
      gpointer *functional_widget_rtn,
      gpointer data,
      void (*func_cb)(GtkWidget *w, gpointer)
);
void GUISetMenuItemDefault(GtkWidget *w);
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,
      gui_menu_bar_item_alignment align
);
void GUIMenuItemSetLabel(GtkWidget *menu_item, const gchar *label);
void GUIMenuItemSetPixmap(GtkWidget *menu_item, guint8 **icon_data);
void GUIMenuItemSetAccelKey(
      GtkWidget *menu_item, GtkAccelGroup *accelgrp,
      guint accel_key, guint accel_mods
);
void GUIMenuItemSetCheck(
      GtkWidget *menu_item, gboolean active, gboolean emit
);
gboolean GUIMenuItemGetCheck(GtkWidget *menu_item);
GtkWidget *GUIMenuAddToMenuBarPixmapH(
      GtkWidget *menu_bar, GtkWidget *menu,
      const gchar *label, guint8 **icon_data,
      gui_menu_bar_item_alignment align
);
void GUIMenuItemSetSubMenu(
      GtkWidget *menu_item, GtkWidget *sub_menu
);


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


static gchar *G_STRCAT(gchar *s, const gchar *s2)
{
      if(s != NULL) {
          if(s2 != NULL) {
            gchar *sr = g_strconcat(s, s2, NULL);
            g_free(s);
            s = sr;
          }
      } else {
          if(s2 != NULL)
            s = STRDUP(s2);
          else
            s = STRDUP("");
      }
      return(s);
}


/*
 *    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("");
      }
}


/*
 *    Blocks input for the given GtkWindow if block is set to TRUE
 *    or else allows input again if block is set to FALSE.
 */
void GUIBlockInput(GtkWidget *w, gboolean block)
{
      GtkWidget *invis;

      if((w != NULL) ? !GTK_IS_WINDOW(w) : TRUE)
          return;

      /* Create the GtkInvisible blocker widget as needed */
      invis = gui_invisible_blocker_widget;
      if(invis == NULL)
      {
          gui_invisible_blocker_widget = invis = gtk_invisible_new();
          if(invis == NULL)
            return;
      }

      /* Block input? */
      if(block)
      {
          /* Increase block input level and check if it is the first
           * blocking level
           */
          gui_block_input_level++;
          if(gui_block_input_level == 1)
          {
            /* This is the first blocking level, so we need to map
             * the GtkInvisible and grab it so that input will be
             * blocked
             */
            gtk_widget_show(invis);
            if(gtk_grab_get_current() != invis)
                gtk_grab_add(invis);
          }
      }
      else
      {
          /* Decrease block input level and check if it is the last
           * blocking level
           */
          gui_block_input_level--;
          if(gui_block_input_level == 0)
          {
            if(gtk_grab_get_current() == invis)
                gtk_grab_remove(invis);
            gtk_widget_hide(invis);
          }

          /* Sanitize block input level underflows */
          if(gui_block_input_level < 0)
          {
            gui_block_input_level = 0;
            g_printerr(
"GUIBlockInput(): Block level underflow to level %i.\n",
                gui_block_input_level
            );
          }
      }
}


/*
 *      DDE Owner data "destroy" signal callback.
 */
static void GUIDDEOwnerDestroyCB(gpointer data)
{
      GtkWidget *w;
      gui_dde_owner_struct *owner = GUI_DDE_OWNER(data);
      if(owner == NULL)
          return;

      w = owner->w;

      g_free(owner->data);
      owner->data = NULL;
      owner->length = 0;

      GTK_SIGNAL_DISCONNECT(w, owner->selection_clear_event_id)
      GTK_SIGNAL_DISCONNECT(w, owner->selection_get_id)

      g_free(owner);
}

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

      /* Remove owner data and disconnect signals since this widget
       * is no longer responsible for sending out DDE data
       */
      gtk_object_remove_data(
          GTK_OBJECT(widget), GUI_DDE_OWNER_DATA_KEY
      );

      return(TRUE);
}

/*
 *      DDE GtkWidget "selection_get" signal callback.
 */
static void GUIDDESelectionGetCB(
      GtkWidget *widget, GtkSelectionData *selection_data,
      guint info, guint t, gpointer data
)
{
      GdkAtom target;
      gui_dde_owner_struct *owner = GUI_DDE_OWNER(data);
      if((widget == NULL) || (selection_data == NULL) ||
         (owner == NULL)
      )
          return;

      /* Handle by owner's selection target type */
      target = owner->target_atom;
      /* GdkBitmap */
      if(target == GDK_TARGET_BITMAP)
      {
/* TODO */
      }
      /* GdkColormap */
      else if(target == GDK_TARGET_COLORMAP)
      {
/* TODO */
      }
      /* GdkDrawable */
      else if(target == GDK_TARGET_DRAWABLE)
      {
/* TODO */
      }
      /* GdkPixmap */
      else if(target == GDK_TARGET_PIXMAP)
      {
/* TODO */
      }
      /* String (latin-1 characters) */
      else if(target == GDK_TARGET_STRING)
      {
          gtk_selection_data_set(
            selection_data,
            owner->type_atom,
            8,
            owner->data,
            owner->length
          );
      }
}

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

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

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

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

      /* Begin handling selection data by its type */
      sel_rtn->type = type = selection_data->type;
      /* Atoms List */
      if(type == GDK_SELECTION_TYPE_ATOM)
      {
          gchar *s = NULL;
          gint i, m = selection_data->length / sizeof(GdkAtom);
          gchar *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 = 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)
      {
          gint len = sel_rtn->length = selection_data->length;
          gchar *s = (gchar *)g_malloc(len + 1);
          memcpy(s, selection_data->data, len);
          s[len] = '\0';
          sel_rtn->data = s;
      }
}


/*
 *    DND drag begin callback, this function is called at the start
 *    of a drag.
 *
 *      The given data is always NULL for now.
 */
static void GUIDNDDragBeginCB(
      GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{
      gui_dnd_icon_struct *dnd_icon = &gui_dnd_icon;
      if((widget == NULL) || (dc == NULL))
          return;

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

      /* Stop the signal so that other callbacks do not override
       * what we have set here
       */
      gtk_signal_emit_stop_by_name(
          GTK_OBJECT(widget), "drag_begin"
      );
}

/*
 *    DND drag motion callback, this function will basically
 *    handle the change of default action on the given destination
 *    widget.
 *
 *    The given data is assumed to be a gui_dnd_cb_struct *.
 */
static gboolean GUIDNDDragMotionCB(
      GtkWidget *widget, GdkDragContext *dc,
      gint x, gint y, guint t,
      gpointer data
)
{
      gboolean same_widget;
      GtkWidget *src_widget, *tar_widget;
      GdkDragAction action;
      gui_dnd_cb_struct *cb_data = GUI_DND_CB(data);
      if((dc == NULL) || (cb_data == NULL))
          return(FALSE);

      /* Get source widget and target widgets */
      src_widget = gtk_drag_get_source_widget(dc);
      tar_widget = widget;
      /* Set marker for if the source and target widgets are the same */
      same_widget = (src_widget == tar_widget) ? TRUE : FALSE;

      /* Get default action from cb_data structure, if source and
       * target are the same widget then use default_action_same
       * otherwise use default_action
       */
      action = same_widget ?
          cb_data->default_action_same : cb_data->default_action;


      /* Respond with default drag action (status)
       *
       * First we check the dc's list of actions, if the list only
       * contains move or copy then we select just that, otherwise
       * we return with our default suggested action
       *
       * If no valid actions are listed then we respond with 0
       */

      /* Only move? */
      if(dc->actions == GDK_ACTION_MOVE)
          gdk_drag_status(dc, GDK_ACTION_MOVE, t);
      /* Only copy? */
      else if(dc->actions == GDK_ACTION_COPY)
          gdk_drag_status(dc, GDK_ACTION_COPY, t);
      /* Only link? */
      else if(dc->actions == GDK_ACTION_LINK)
          gdk_drag_status(dc, GDK_ACTION_LINK, t);
      /* Other action, check if listed in our actions list? */
      else if(dc->actions & action)
          gdk_drag_status(dc, action, t);
      /* All else respond with 0 */
      else
          gdk_drag_status(dc, 0, t);

      /* Is target widget valid and sensitive? */
      if((tar_widget != NULL) ? GTK_WIDGET_SENSITIVE(tar_widget) : FALSE)
      {
          /* Set target widget into focus as needed */
          if(!GTK_WIDGET_HAS_FOCUS(tar_widget) &&
             GTK_WIDGET_CAN_FOCUS(tar_widget)
          )
            gtk_widget_grab_focus(tar_widget);

          /* Handle rest by the target widget's type */
          /* Target widget is a GtkCTree? */
          if(GTK_IS_CTREE(tar_widget))
          {
            /* Reference it as a GtkCList */
            GtkCList *clist = (GtkCList *)tar_widget;
            gint row, column;

            /* Update flags as needed */
            clist->flags |= GTK_CLIST_DRAW_DRAG_LINE;
            clist->flags |= GTK_CLIST_DRAW_DRAG_RECT;

            /* Calculate column row from x and y coordinates */
            if(!gtk_clist_get_selection_info(
                clist,
                x,
                y - ((clist->flags & GTK_CLIST_SHOW_TITLES) ?
                  clist->column_title_area.height +
                  clist->column_title_area.y : 0),
                &row, &column
            ))
            {
                row = -1;
                column = -1;
            }

#if 0
            i = 0;
            glist = clist->row_list;
            while(glist != NULL)
            {
                if(i == row)
                {
                  row_ptr = (GtkCListRow *)glist->data;
                  break;
                }
                i++;
                glist = g_list_next(glist);
            }

            if(row_ptr != NULL)
                gtk_signal_emit_by_name(
                  GTK_OBJECT(tar_widget), "draw_drag_highlight",
                  row_ptr,
                  row,
                  GTK_CLIST_DRAG_INTO
                );
#endif
            /* Change in drag position? */
            if(row != clist->focus_row)
            { 
                clist->focus_row = row;
                gtk_widget_queue_draw(tar_widget);
            }
          }
          /* Target widget is a GtkCList? */
          else if(GTK_IS_CLIST(tar_widget))
          {
            GtkCList *clist = (GtkCList *)tar_widget;
            gint row, column;


            /* Update flags as needed */
            clist->flags |= GTK_CLIST_DRAW_DRAG_LINE;
            clist->flags |= GTK_CLIST_DRAW_DRAG_RECT;

            /* Calculate column row from x and y coordinates */
            if(!gtk_clist_get_selection_info(
                clist,
                x,
                y - ((clist->flags & GTK_CLIST_SHOW_TITLES) ?
                  clist->column_title_area.height +
                  clist->column_title_area.y : 0),
                &row, &column
            ))
            {
                row = -1;
                column = -1;
            }

            /* Change in drag position? */
            if(row != clist->focus_row)
            {
                clist->focus_row = row;
                gtk_widget_queue_draw(tar_widget);
            }
          }
      }

      return(FALSE);
}

/*
 *      DND drag drop callback.
 *
 *      The given data is assumed to be a gui_dnd_cb_struct *.
 */
static gboolean GUIDNDDragDropCB(
      GtkWidget *widget, GdkDragContext *dc,
      gint x, gint y, guint t,
      gpointer data
)
{
      gui_dnd_cb_struct *cb_data = GUI_DND_CB(data);
      if((dc == NULL) || (cb_data == NULL))
          return(FALSE);

      return(FALSE);
}

/*
 *      DND drag data received callback.
 *
 *      The given data is assumed to be a gui_dnd_cb_struct *.
 */
static void GUIDNDDragReceivedCB(
      GtkWidget *widget, GdkDragContext *dc,
      gint x, gint y,
      GtkSelectionData *selection_data, guint info, guint t,
      gpointer data
)
{
      gui_dnd_cb_struct *cb_data = GUI_DND_CB(data);
      if((dc == NULL) || (cb_data == NULL))
          return;

}

/*
 *      DND drag end callback, this function will reset the target
 *    widget as needed and clean up any additional resources.
 *
 *    The given data is always NULL for now.
 */
static void GUIDNDDragEndCB(
      GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{
      gboolean same_widget;
      GtkWidget *src_widget, *tar_widget;
      if(dc == NULL)
          return;


      /* Get source widget and target widgets */
      src_widget = gtk_drag_get_source_widget(dc);
      tar_widget = widget;
      /* Set marker for if the source and target widgets are the same */
      same_widget = (src_widget == tar_widget) ? TRUE : FALSE;


      /* Is target widget valid and sensitive? */
      if((tar_widget != NULL) ? GTK_WIDGET_SENSITIVE(tar_widget) : FALSE)
      {
          /* Handle rest by the target widget's type */
          /* Is target widget a ctree? */
          if(GTK_IS_CTREE(tar_widget))
          {
            GtkCList *clist = GTK_CLIST(tar_widget);

            clist->flags &= ~GTK_CLIST_DRAW_DRAG_LINE;
            clist->flags &= ~GTK_CLIST_DRAW_DRAG_RECT;
            gtk_widget_queue_draw(tar_widget);
          }
          /* Is target widget a clist? */
          else if(GTK_IS_CLIST(tar_widget))
          {
            GtkCList *clist = GTK_CLIST(tar_widget);

            clist->flags &= ~GTK_CLIST_DRAW_DRAG_LINE;
            clist->flags &= ~GTK_CLIST_DRAW_DRAG_RECT;
            gtk_widget_queue_draw(tar_widget);
          }
      }
}


/*
 *    Button data destroy callback.
 */
static void GUIButtonDataDestroyCB(gpointer data)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(data);
      if(btn_data == NULL)
          return;

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


/*
 *    Banner event signal callback.
 */
static gint GUIBannerEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      gint status = FALSE;
      const gchar *label;
      GdkFont *font;
      GtkJustification justify;
      GdkEventConfigure *configure;
      GdkEventExpose *expose;
      gpointer *cb_data = (gpointer *)data;
      if((widget == NULL) || (event == NULL) || (cb_data == NULL))
          return(status);

      label = (const gchar *)cb_data[0];
      justify = (GtkJustification)cb_data[1];
      font = (GdkFont *)cb_data[2];

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

        case GDK_EXPOSE:
          expose = (GdkEventExpose *)event;
          GUIBannerDraw(widget);
          status = TRUE;
          break;
      }

      return(status);
}

/*
 *    Banner data destroy callback.
 */
static void GUIBannerDataDestroyCB(gpointer data)
{
      gchar *label;
      GtkJustification justify;
      GdkFont *font;
      gpointer *cb_data = (gpointer *)data;
      if(cb_data == NULL)
          return;

      label = (gchar *)cb_data[0];
      justify = (GtkJustification)cb_data[1];
      font = (GdkFont *)cb_data[2];

      g_free(label);
      GDK_FONT_UNREF(font)

      g_free(cb_data);
}


/*
 *      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, this function will
 *    take the given data pointer as a dynamically allocated
 *    (void **).
 */
static void GUIMenuActivateCB(gpointer data)
{
      gint argc = 3;
      GtkWidget *functional_widget;
      gpointer client_data;
      void (*func_cb)(GtkWidget *, gpointer);
      gpointer *cb_data = (gpointer *)data;
      if(cb_data == NULL)
          return;

      /* Data Format:
       *
       * w                    Functional Widget
       * client_data                Data
       * func_cb              Callback Function
       * NULL
       */
      functional_widget = (GtkWidget *)((argc > 0) ? cb_data[0] : NULL);
      client_data = ((argc > 1) ? cb_data[1] : NULL);
      func_cb = ((argc > 2) ? cb_data[2] : NULL);

      if(func_cb != NULL)
          func_cb(
            functional_widget,      /* Functional Widget */
            client_data       /* Data */
          );
}

/*
 *    Menu item client data destroy callback.
 */
static void GUIMenuClientDataDestroyCB(gpointer data)
{
      gpointer *mi_client_data = (gpointer *)data;
      g_free(mi_client_data);
}

/*
 *    Menu item data destroy callback.
 */
static void GUIMenuDataDestroyCB(gpointer data)
{
      gui_menu_item_data_struct *mi = GUI_MENU_ITEM_DATA(data);
      if(mi == NULL)
          return;

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

/*
 *    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(gui_style_default == NULL)
          gui_style_default = gtk_widget_get_default_style();

      if((widget != NULL) ? GTK_WIDGET_NO_WINDOW(widget) : TRUE)
          return;

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

      style = gtk_widget_get_style(widget);
      if(style == NULL)
          style = gui_style_default;
      if(style == NULL)
          return;

      gdk_window_clear(window);
#ifndef HAVE_WIN32
      gtk_draw_handle(
          style, window,
          GTK_STATE_NORMAL,         /* State type */
          GTK_SHADOW_OUT,           /* Shadow type */
          0, 0,
          widget->allocation.width,
          widget->allocation.height,
          GTK_ORIENTATION_HORIZONTAL
      );
#endif
}

/*
 *    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);
            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_window_set_policy(
                GTK_WINDOW(w),
                TRUE, TRUE, FALSE
            );
            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 given GtkCList cell specified by column
 *    and row.
 *
 *    Returns TRUE if the cell geometry was obtained or FALSE on
 *    failed match or error.
 */
gboolean GUICListGetCellGeometry(
      GtkCList *clist, gint column, 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);
}


/*
 *    Returns the row index of the specified node.
 */
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);
}


/*
 *    Used by GUICTreeNodeDeltaRows().
 */
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;
      }
}

/*
 *    Returns the number of rows between the GtkCTree nodes.
 */
gint GUICTreeNodeDeltaRows(
      GtkCTree *ctree,
      GtkCTreeNode *start,    /* Use NULL for first/toplevel node */
      GtkCTreeNode *end
)
{
      gboolean end_found = FALSE;
      gint row_count = 0;

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

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

      GUICTreeNodeDeltaRowsIterate(
          ctree, start, end, &row_count, &end_found
      );

      return(row_count);
}

/*
 *    Returns the coordinate position for the specified GdkWindow
 *    relative to the root GdkWindow.
 */
void GUIGetWindowRootPosition(
      GdkWindow *window, gint *x, gint *y
)
{
      gint cx, cy;
      GdkWindow *cur_w = window, *root_w;


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

      if(cur_w == NULL)
          return;

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      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 != NULL)
            *x += cx;
          if(y != NULL)
            *y += cy;
          cur_w = gdk_window_get_parent(cur_w);
      }
}

/*
 *    Converts the coordinates x and y (relative to the window w)
 *    be relative to the root window.
 */
void GUIGetPositionRoot(
      GdkWindow *w, gint x, gint y, gint *rx, gint *ry
)
{
      GdkWindow *cur = w, *root_w;
      gint cx, cy;


      if(rx != NULL)
          *rx = 0;
      if(ry != NULL)
          *ry = 0;

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      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(rx != NULL)
          *rx = x;
      if(ry != NULL)
          *ry = y;
}


/*
 *    Gets the text bounds.
 */
void GUIGetTextBounds(
      GdkFont *font, const gchar *text, 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 GC
 */
GdkGC *GDK_GC_NEW(void)
{
      if(gui_window_root == NULL) 
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      return(gdk_gc_new(gui_window_root));
}


/*
 *    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 WM icon for the GdkWindow w based on the specified XPM
 *    data.
 */
void GUISetWMIcon(GdkWindow *window, guint8 **data)
{
      GdkBitmap *mask;
      GdkPixmap *pixmap;

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

      /* Load icon pixmap and mask */
      pixmap = GDK_PIXMAP_NEW_FROM_XPM_DATA(&mask, data);
      if(pixmap != NULL)
      {
          /* Set WM icon 
           *
           * Note that gdk_window_set_icon() will take care of 
           * unref'ing the pixmap and mask
           */
          gdk_window_set_icon(window, NULL, pixmap, mask);
      }
}

/*
 *    Sets the WM icon for the GdkWindow w based on the specified XPM
 *    file.
 */
void GUISetWMIconFile(GdkWindow *window, const gchar *filename)
{ 
      GdkBitmap *mask;
      GdkPixmap *pixmap;

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

      /* Load icon pixmap and mask */
      pixmap = GDK_PIXMAP_NEW_FROM_XPM_FILE(&mask, filename);
      if(pixmap != NULL)
      {
          /* Set WM icon
           *
           * Note that gdk_window_set_icon() will take care of
           * unref'ing the pixmap and mask
           */
          gdk_window_set_icon(window, NULL, 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, 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;

#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(GTK_WIDGET(w)) ||
         GTK_WIDGET_NO_WINDOW(GTK_WIDGET(w))
      )
          return;

      window = GTK_WIDGET(w)->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;
                GdkGeometryFlags flags = gdk_parse_geometry(
                  arg, &x, &y, &width, &height
                );
                geometry_flags = flags;
                if(flags)
                {
                  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 && (geometry != NULL))
      {
          GdkGeometryFlags flags = geometry_flags;
          GdkRectangle *rect = geometry;
          if((flags & GDK_GEOMETRY_X) || (flags & GDK_GEOMETRY_Y))
            gtk_widget_set_uposition(
                GTK_WIDGET(w), rect->x, rect->y
            );
          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
            );
      }

      /* 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
}


/*
 *    Sets the GtkCTree's position to optimize the viewing of all
 *    child nodes of the given node.
 *
 *    Returns TRUE if the position has changed.
 */
gboolean GUICTreeOptimizeExpandPosition(
      GtkCTree *ctree,
      GtkCTreeNode *node      /* Expanded parent node */
)
{
      gboolean need_scroll = FALSE;
      gint row_height, rows_visible, child_rows;
      GtkCList *clist;
      GtkCTreeRow *row_ptr;
      GtkCTreeNode *cur_node, *last_child_node, *scroll_end_node = NULL;

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

      /* Get clist information */
      clist = GTK_CLIST(ctree);
      if(clist->columns <= 0)
          return(FALSE);
      if(GTK_CLIST_ROW_HEIGHT_SET(clist))
          row_height = clist->row_height + 1;   /* Assume uniform spaced
                                     * rows and include border */
      else
          row_height = 20;
      if(row_height <= 0)
          return(FALSE);
      rows_visible = clist->clist_window_height / row_height;
      if(rows_visible <= 0)
          return(FALSE);

      /* Get first child of expanded node */
      last_child_node = NULL;
      child_rows = 1;               /* Include 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;
          child_rows++;
          if(child_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 visible */
      if(!need_scroll && (last_child_node != NULL))
      {
          const gint    rows = GUICTreeNodeDeltaRows(
            ctree, NULL, node) + child_rows,
                  y = (rows * row_height) -
            (gint)GTK_ADJUSTMENT_GET_VALUE(clist->vadjustment);
          if(y > clist->clist_window_height)
          {
            need_scroll = TRUE;
            scroll_end_node = gtk_ctree_node_nth(ctree, rows - 1);
          }
      }

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


/*
 *    Creates a bitmap from the given RGBA data
 *
 *    Only the alpha byte will be used to create the bitmap.
 *
 *    An alpha byte value greater than or equal to the value
 *    specified by threshold will set a 1 bit in the bitmap and a
 *    value less will set a 0 bit.
 *
 *    If bytes per line bpl is -1, then it will be automatically
 *    calculated.
 */
GdkBitmap *GUICreateBitmapFromDataRGBA(
      gint width, gint height, gint bpl,
      const guint8 *rgba, guint8 threshold,
      GdkWindow *window
)
{
      const gint bpp = 4;
      gint xbm_bpl;
      guint8 *xbm_data;
      const guint8 *src_ptr;
      gint x, y;
      GdkBitmap *bitmap;

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();

      if(window == NULL)
          window = gui_window_root;

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

      /* Calculate bytes per line, the number of bytes to be able to
       * hold one whole line of bits
       */
      xbm_bpl = (width / 8) + ((width % 8) ? 1 : 0);

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

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

      /* Begin converting the RGBA data to the bitmap data */
      for(y = 0; y < height; y++)
      {
          for(x = 0; x < width; x++)
          {
            src_ptr = &rgba[
                (y * bpl) + (x * bpp)
            ];
            /* Alpha value greater or equal to threshold? */
            if(src_ptr[3] >= threshold)
                xbm_data[
                  (y * xbm_bpl) + (x / 8)
                ] |= (guint8)(1 << (x % 8));
          }
      }

      /* Create bitmap */
      bitmap = gdk_bitmap_create_from_data(
          window, xbm_data,
          width, height
      );
      g_free(xbm_data); /* No longer needed */

      return(bitmap);
}


/*
 *    Creates a new GdkPixmap of the specified size and current
 *    depth.
 */
GdkPixmap *GDK_PIXMAP_NEW(gint width, gint height)
{
      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();

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

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

/*
 *    Creates a new GdkPixmap from the specified XPM data.
 */
GdkPixmap *GDK_PIXMAP_NEW_FROM_XPM_DATA(
      GdkBitmap **mask_rtn, guint8 **data
)
{
      GdkPixmap *pixmap;
      GdkBitmap *mask;
      GdkWindow *window;
      GtkStyle *style;

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

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

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      if(gui_style_default == NULL)
          gui_style_default = gtk_widget_get_default_style();

      window = gui_window_root;
      style = gui_style_default;

      /* Create a new pixmap from data */
      pixmap = gdk_pixmap_create_from_xpm_d(
          window, &mask,
          (style != NULL) ? &style->bg[GTK_STATE_NORMAL] : NULL,
          (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 the specified XPM file.
 */
GdkPixmap *GDK_PIXMAP_NEW_FROM_XPM_FILE(
      GdkBitmap **mask_rtn, const gchar *filename
)
{
      GdkPixmap *pixmap;
      GdkBitmap *mask;
      GdkWindow *window;
      GtkStyle *style;

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

      if(STRISEMPTY(filename))
          return(NULL);

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      if(gui_style_default == NULL)
          gui_style_default = gtk_widget_get_default_style(); 

      window = gui_window_root;
      style = gui_style_default;

      /* Create a new pixmap from file */
      pixmap = gdk_pixmap_create_from_xpm(
          window, &mask,
          (style != NULL) ? &style->bg[GTK_STATE_NORMAL] : NULL,
          filename
      );
      if(pixmap == NULL)
          return(NULL);

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

      return(pixmap);
}

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

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      if(gui_style_default == NULL)
          gui_style_default = gtk_widget_get_default_style();

      if(window == NULL)
          window = gui_window_root;

      if(transparent_color == NULL)
      {
          GtkStyle *style = gui_style_default;
          if(style != NULL)
            transparent_color = &style->bg[GTK_STATE_NORMAL];
      }

      /* Load pixmap and mask */
      pixmap = gdk_pixmap_create_from_xpm_d(
          window, &mask, transparent_color, (gchar **)data
      );
      if(pixmap != NULL)
      {
          /* Create new GtkPixmap from the loaded pixmap and mask */
          w = gtk_pixmap_new(pixmap, mask);
      }
      else
      {
          w = NULL;
      }

      /* Unref pixmap and mask pair, they are no longer needed */
      GDK_PIXMAP_UNREF(pixmap);
      GDK_BITMAP_UNREF(mask);

      return(w);
}

/*
 *    Creates a new GtkPixmap from the given XPM data.
 */
GtkWidget *GUICreateMenuItemIcon(guint8 **icon_data)
{
      gint width = 0, height = 0;
      GdkWindow *window;
      GdkPixmap *pixmap = NULL;
      GdkBitmap *mask = NULL;
      GtkStyle *style;
      GtkWidget *w = NULL;


      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      if(gui_style_default == NULL)
          gui_style_default = gtk_widget_get_default_style();

      window = gui_window_root;
      style = gui_style_default;

      /* If no icon data specified, then use default empty icon */
      if(icon_data == NULL)
      {
          gchar *bitmap_data;

          /* Set width and height of blank icon */
          width = GUI_MENU_ITEM_DEF_HEIGHT;
          height = width;

          /* Allocate tempory bitmap data and create the GdkBitmap mask */
          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
            );
          else
            mask = NULL;
          /* Delete tempory bitmap data, it is no longer needed */
          g_free(bitmap_data);

          /* Create blank GdkPixmap */
          pixmap = gdk_pixmap_new(
            window, width, height, -1
          );

          /* Pixmap created successfully? */
          if(pixmap != NULL)
          {
            /* Create a GtkPixmap based on new pixmap and mask pair */
            w = gtk_pixmap_new(pixmap, mask);
          }
      }
      else
      {
          /* Create pixmap and mask pair from given data */
          pixmap = gdk_pixmap_create_from_xpm_d(
              window, &mask,
            (style != NULL) ?
                &style->bg[GTK_STATE_NORMAL] : NULL,
              (gchar **)icon_data
          );

          /* Pixmap created successfully? */
          if(pixmap != NULL)
          {
            /* Create a GtkPixmap based on new pixmap and mask pair */
            w = gtk_pixmap_new(pixmap, mask);

            /* Get size of newly loaded pixmap */
            gdk_window_get_size((GdkWindow *)pixmap, &width, &height);
          }
      }

      /* Unref pixmap and mask pair, they are no longer needed */
      GDK_PIXMAP_UNREF(pixmap);
      GDK_BITMAP_UNREF(mask);

      return(w);
}


/*
 *    Maps 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 tip for the given widget w, if the widget w already
 *    has a tip set then it will be changed to the new value.
 *
 *    If tip is NULL then no tip will be set on the given widget.
 */
void GUISetWidgetTip(GtkWidget *w, const gchar *tip)
{
      if(w == NULL)
          return;

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

      if(tip != NULL)
      {
          gtk_tooltips_set_tip(
            gui_tooltips,           /* Our tooltips group */
            w,
            tip, NULL
          );
      }
      else
      {
          gtk_tooltips_set_tip(
            gui_tooltips,           /* Our tooltips group */
            w,
            NULL, NULL
          );
      }
}

/*
 *    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;
      else
          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 global tooltips state.
 */
void GUISetGlobalTipsState(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, gboolean show_pixmap, gboolean show_label)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      if(btn_data == NULL)
          return;

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

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

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

/*
 *    Underlines the specified character in the button's label.
 */
void GUIButtonLabelUnderline(GtkWidget *w, guint c)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      if(btn_data == NULL)
          return;

      if((btn_data->label != NULL) && (btn_data->label_text != NULL))
      {
          GtkWidget *w = btn_data->label;
          const gchar *text = btn_data->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;
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      if(btn_data == NULL)
          return;

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();

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

      /* Update icon? */
      if((icon != NULL) &&
         ((const guint8 **)icon != btn_data->icon_data)
      )
      {
          GtkStyle *style = gtk_widget_get_style(btn_data->button);
          GdkWindow *window = gui_window_root;
          if((style != NULL) && (window != NULL))
          {
            GdkBitmap *mask;
            GdkPixmap *pixmap = gdk_pixmap_create_from_xpm_d(
                window, &mask,
                &style->bg[GTK_STATE_NORMAL],
                (gchar **)icon
            );
            if(pixmap != NULL)
            {
                gint width, height;
                gdk_window_get_size(pixmap, &width, &height);

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

                /* Set new icon pixmap */
                w = btn_data->pixmap;
                if(w != NULL)
                {
                  gtk_pixmap_set(GTK_PIXMAP(w), pixmap, mask);
                }
                else
                {
                  btn_data->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)
                  );
                }

                /* Unref the loaded pixmap and mask pair, they are
                 * no longer needed
                 */
                GDK_PIXMAP_UNREF(pixmap);
                GDK_BITMAP_UNREF(mask);
            }
          }
      }

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

/*
 *    Changes the button's arrow and/or label.
 */
void GUIButtonArrowUpdate(
      GtkWidget *w,
      GtkArrowType arrow_type, gint arrow_width, gint arrow_height,
      const gchar *label
)
{
      GtkWidget *parent;
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      if(btn_data == NULL)
          return;

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

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

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

/*
 *    Gets the button's main GtkBox.
 */
GtkWidget *GUIButtonGetMainBox(GtkWidget *w)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      return((btn_data != NULL) ? btn_data->main_box : NULL);
}

/*
 *      Gets the button's GtkLabel.
 */
GtkWidget *GUIButtonGetLabel(GtkWidget *w)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      return((btn_data != NULL) ? btn_data->label : NULL);
}

/*
 *    Gets the button's GtkPixmap.
 */
GtkWidget *GUIButtonGetPixmap(GtkWidget *w)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      return((btn_data != NULL) ? btn_data->pixmap : NULL);
}

/*
 *    Gets the button's GtkArrow.
 */
GtkWidget *GUIButtonGetArrow(GtkWidget *w)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      return((btn_data != NULL) ? btn_data->arrow : NULL);
}

/*
 *    Gets the button's label text.
 */
gchar *GUIButtonGetLabelText(GtkWidget *w)
{
      gui_button_data_struct *btn_data = GUI_BUTTON_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_BUTTON_DATA_KEY)
      );
      return((btn_data != NULL) ? btn_data->label_text : NULL);
}

/*
 *    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;
      gui_button_data_struct *btn_data;

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

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();

      /* Create callback data */
      btn_data = GUI_BUTTON_DATA(
          g_malloc0(sizeof(gui_button_data_struct))
      );

      /* Create button */
      btn_data->button = w = gtk_button_new();
      gtk_object_set_data_full(
          GTK_OBJECT(w), GUI_BUTTON_DATA_KEY,
          btn_data, GUIButtonDataDestroyCB
      );
      parent = w;

      /* Create main box */
      if(horizontal)
          w = gtk_hbox_new(FALSE, 0);
      else
          w = gtk_vbox_new(FALSE, 0);
      gtk_container_border_width(GTK_CONTAINER(w), 0);
      gtk_container_add(GTK_CONTAINER(parent), w);
      gtk_widget_show(w);
      btn_data->main_box = parent = w;

      /* Create icon? */
      if(icon != NULL)
      {
          GdkWindow *window = gui_window_root;
          GtkStyle *style = gtk_widget_get_style(btn_data->button);
          if(style != NULL)
          {
            GdkBitmap *mask;
            GdkPixmap *pixmap = gdk_pixmap_create_from_xpm_d(
                window, &mask,
                &style->bg[GTK_STATE_NORMAL], (gchar **)icon
            );
            if(pixmap != NULL)
            {
                gint width, height;
                gdk_window_get_size(pixmap, &width, &height);

                btn_data->icon_data = (const guint8 **)icon;

                btn_data->pixmap = w = gtk_pixmap_new(pixmap, mask);
                gtk_widget_set_usize(
                  w,
                  width + (2 * 0),
                  height + (2 * 0)
                );
                gtk_box_pack_start(
                  GTK_BOX(parent), w, TRUE, FALSE, border
                );
                gtk_widget_show(w);

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

      /* Create label? */
      if(label != NULL)
      {
          btn_data->label_text = STRDUP(label);
          btn_data->label = w = gtk_label_new(btn_data->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(btn_data->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(
      GtkArrowType arrow_type, gint arrow_width, gint arrow_height,
      const gchar *label, GtkWidget **label_rtn,
      gboolean horizontal
)
{
      const gint border = (label != NULL) ? 1 : 0;
      GtkWidget *w, *parent;
      gui_button_data_struct *btn_data;

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

      /* Create callback data */
      btn_data = GUI_BUTTON_DATA(
          g_malloc0(sizeof(gui_button_data_struct))
      );

      /* Create button */
      btn_data->button = w = gtk_button_new();
      gtk_object_set_data_full(
          GTK_OBJECT(w), GUI_BUTTON_DATA_KEY,
          btn_data, GUIButtonDataDestroyCB
      );
      parent = w;

      /* Create main box */
      if(horizontal)
          w = gtk_hbox_new(FALSE, 0);
      else
          w = gtk_vbox_new(FALSE, 0);
      gtk_container_border_width(GTK_CONTAINER(w), 0);
      gtk_container_add(GTK_CONTAINER(parent), w);
      gtk_widget_show(w);
      btn_data->main_box = parent = w;

      /* Create arrow */
      btn_data->arrow_type = arrow_type;
      btn_data->arrow = w = gtk_arrow_new(
          btn_data->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);

      /* Create label? */
      if(label != NULL)
      {
          btn_data->label_text = STRDUP(label);
          btn_data->label = w = gtk_label_new(btn_data->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(btn_data->button);
}

/*
 *    Creates a horizontal GtkButton with the specified arrow and
 *    label.
 */
GtkWidget *GUIButtonArrowLabelH(
      GtkArrowType arrow_type, gint arrow_width, 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(
      GtkArrowType arrow_type, gint arrow_width, 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(
      GtkArrowType arrow_type, gint arrow_width, 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,
      gboolean horizontal
)
{
      const gint border = (label != NULL) ? 1 : 0;
      GtkWidget *w, *parent;
      gui_button_data_struct *btn_data;

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

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();

      /* Create callback data */
      btn_data = GUI_BUTTON_DATA(
          g_malloc0(sizeof(gui_button_data_struct))
      );

      /* Create button */
      btn_data->button = w = gtk_toggle_button_new();
      gtk_object_set_data_full(
          GTK_OBJECT(w), GUI_BUTTON_DATA_KEY,
          btn_data, GUIButtonDataDestroyCB
      );
      parent = w;

      /* Create main box */
      if(horizontal)
          w = gtk_hbox_new(FALSE, 0);
      else
          w = gtk_vbox_new(FALSE, 0);
      gtk_container_border_width(GTK_CONTAINER(w), 0);
      gtk_container_add(GTK_CONTAINER(parent), w);
      gtk_widget_show(w);
      btn_data->main_box = parent = w;

      /* Create icon? */
      if(icon != NULL)
      {
          GdkWindow *window = gui_window_root;
          GtkStyle *style = gtk_widget_get_style(btn_data->button);
          if(style != NULL)
          {
            GdkBitmap *mask;
            GdkPixmap *pixmap = gdk_pixmap_create_from_xpm_d(
                window, &mask,
                &style->bg[GTK_STATE_NORMAL], (gchar **)icon
            );
            if(pixmap != NULL)
            {
                gint width, height;
                gdk_window_get_size(pixmap, &width, &height);

                btn_data->icon_data = (const guint8 **)icon;

                btn_data->pixmap = w = gtk_pixmap_new(pixmap, mask);
                gtk_widget_set_usize(
                  w,
                  width + (2 * 0),
                  height + (2 * 0)
                );
                gtk_box_pack_start(
                  GTK_BOX(parent), w, TRUE, FALSE, border
                );
                gtk_widget_show(w);

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

      /* Create label? */
      if(label != NULL)
      {
          btn_data->label_text = STRDUP(label);

          btn_data->label = w = gtk_label_new(btn_data->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(btn_data->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 *parent, *w;

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

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();

      /* 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)
      {
          GdkWindow *window = gui_window_root;
          GtkStyle *style = gtk_widget_get_style(parent);
          GdkBitmap *mask;
          GdkPixmap *pixmap = gdk_pixmap_create_from_xpm_d(
            window, &mask,
            (style != NULL) ? &style->bg[GTK_STATE_NORMAL] : NULL,
            (gchar **)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_browse_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
      ));
}


/*
 *    Sets the DDE as binary data.
 */
void GUIDDESetBinary(
      GtkWidget *w,                 /* Owner widget */
      GdkAtom selection_atom,       /* Can be GDK_NONE */
      gulong t,               /* Time */
      const guint8 *data, gint length
)
{
      static GdkAtom target_atom = GDK_NONE;
      guint8 *datau8;
      gui_dde_owner_struct *owner;


      if(target_atom == GDK_NONE)
          target_atom = gdk_atom_intern("STRING", FALSE);

      if(selection_atom == GDK_NONE)
          selection_atom = GDK_SELECTION_PRIMARY;

      if((w == NULL) || (data == NULL) || (length <= 0))
          return;

      if(GTK_WIDGET_NO_WINDOW(w))
      {
          g_printerr(
"GUIDDESetBinary(): Failed:\
 GtkWidget 0x%.8x does not have a GdkWindow.\n",
            (guint32)w
          );
          return;
      }

      /* Allocate callback data */
      owner = GUI_DDE_OWNER(g_malloc0(sizeof(gui_dde_owner_struct)));
      owner->w = w;
      owner->target_atom = target_atom;
      owner->type_atom = target_atom;
      owner->selection_clear_event_id = gtk_signal_connect(
          GTK_OBJECT(w), "selection_clear_event",
          GTK_SIGNAL_FUNC(GUIDDESelectionClearEventCB),
          owner
      );
      owner->selection_get_id = gtk_signal_connect(
          GTK_OBJECT(w), "selection_get",
          GTK_SIGNAL_FUNC(GUIDDESelectionGetCB),
          owner
      );
      datau8 = (guint8 *)g_malloc((length + 1) * sizeof(guint8));
      memcpy(datau8, data, length * sizeof(guint8));
      datau8[length] = '\0';
      owner->data = datau8;
      owner->length = length;

      /* Set owner callback data on the widget, destroying any
       * previously set owner callback data on the widget
       */
      gtk_object_set_data_full(
          GTK_OBJECT(w), GUI_DDE_OWNER_DATA_KEY,
          owner, GUIDDEOwnerDestroyCB
      );

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

      /* Set selection owner */
      if(!gtk_selection_owner_set(
          w,                  /* Owner GtkWidget */
          selection_atom,     /* Selection */
          (guint32)t          /* Time */
      ))
      {
          /* Failed to set selection owner */
          gtk_object_remove_data(
            GTK_OBJECT(w), GUI_DDE_OWNER_DATA_KEY
          );
          return;
      }
}

/*
 *    Gets the DDE as binary data.
 */
guint8 *GUIDDEGetBinary(
      GtkWidget *w,                 /* Widget making request */
      GdkAtom selection_atom,       /* Can be GDK_NONE */
      gulong t,               /* Time */
      gint *length
)
{
      guint8 *data;
      gint len;
      guint sig_id;
      GdkAtom type;
      GtkSelectionData *selection_data;
      static GdkAtom target_atom = GDK_NONE;

      if(target_atom == GDK_NONE)
          target_atom = gdk_atom_intern("STRING", FALSE);

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

      if(selection_atom == GDK_NONE)
          selection_atom = GDK_SELECTION_PRIMARY;

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

      if(GTK_WIDGET_NO_WINDOW(w))
      {
          g_printerr(
"GUIDDEGetBinary(): Failed:\
 GtkWidget 0x%.8x does not have a GdkWindow.\n",
            (guint32)w
          );
          return(NULL);
      }

      /* Set up callback data */
      selection_data = (GtkSelectionData *)g_malloc0(
          sizeof(GtkSelectionData)
      );
      selection_data->selection = selection_atom;
      selection_data->target = target_atom;
      selection_data->type = GDK_NONE;
      selection_data->format = -1;
      selection_data->data = NULL;
      selection_data->length = -1;  /* -1 means data not received */

      /* Set up widget to receive the "selection_received" signal */
      sig_id = gtk_signal_connect(
          GTK_OBJECT(w), "selection_received",
          GTK_SIGNAL_FUNC(GUIDDESelectionReceivedCB),
          selection_data
      );

      /* Request selection */
      if(!gtk_selection_convert(
          w,                  /* Widget */
          selection_atom,     /* Selection */
          target_atom,  /* Target */
          (guint32)t          /* Time */
      ))
      {
          /* Selection request failed */
          GTK_SIGNAL_DISCONNECT(GTK_OBJECT(w), sig_id)
          g_free(selection_data->data);
          g_free(selection_data);
          return(NULL);
      }

      /* Wait for selection */
      while(selection_data->length < 0)
      {
          while(gtk_events_pending() > 0)
            gtk_main_iteration();
      }
      /* Got selection */

      /* Disconnect signals */
      GTK_SIGNAL_DISCONNECT(GTK_OBJECT(w), sig_id);

      /* Get data from selection from callback data */
      type = selection_data->type;

      /* Integer */
      if(type == GDK_SELECTION_TYPE_INTEGER)
      {
          data = (guint8 *)selection_data->data;
          selection_data->data = NULL;
          len = selection_data->length;
      }
      /* String (latin-1 characters) */
      else if(type == GDK_SELECTION_TYPE_STRING)
      {
          data = (guint8 *)selection_data->data;
          selection_data->data = NULL;
          len = selection_data->length;
      }
      else
      {
          data = NULL;
          len = 0;
      }

      /* Delete callback data */
      g_free(selection_data->data);
      g_free(selection_data);

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

      return(data);
}

/*
 *    Sets the DDE as a string.
 */
void GUIDDESetString(
      GtkWidget *w,                 /* Owner widget */
      GdkAtom selection_atom,       /* Selection */
      gulong t,               /* Time */
      const gchar *data
)
{
      static GdkAtom target_atom = GDK_NONE;
      gui_dde_owner_struct *owner;


      if(target_atom == GDK_NONE)
          target_atom = gdk_atom_intern("STRING", FALSE);

      if(selection_atom == GDK_NONE)
          selection_atom = GDK_SELECTION_PRIMARY;

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

      if(GTK_WIDGET_NO_WINDOW(w))
      {
          g_printerr(
"GUIDDESetString(): Failed:\
 GtkWidget 0x%.8x does not have a GdkWindow.\n",
            (guint32)w
          );
          return;
      }

      /* Allocate callback data */
      owner = GUI_DDE_OWNER(g_malloc0(sizeof(gui_dde_owner_struct)));
      owner->w = w;
      owner->target_atom = target_atom;
      owner->type_atom = target_atom;
      owner->selection_clear_event_id = gtk_signal_connect(
          GTK_OBJECT(w), "selection_clear_event",
          GTK_SIGNAL_FUNC(GUIDDESelectionClearEventCB),
          owner
      );
      owner->selection_get_id = gtk_signal_connect(
          GTK_OBJECT(w), "selection_get",
          GTK_SIGNAL_FUNC(GUIDDESelectionGetCB),
          owner
      );
      owner->data = STRDUP(data);
      owner->length = STRLEN(owner->data);

      /* Set owner callback data on the widget, destroying any
       * previously set owner callback data on the widget
       */
      gtk_object_set_data_full(
          GTK_OBJECT(w), GUI_DDE_OWNER_DATA_KEY,
          owner, GUIDDEOwnerDestroyCB
      );

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

      /* Set selection owner */
      if(!gtk_selection_owner_set(
          w,                  /* Owner GtkWidget */
          selection_atom,     /* Selection */
          (guint32)t          /* Time */
      ))
      {
          /* Failed to set selection owner */
          gtk_object_remove_data(
            GTK_OBJECT(w), GUI_DDE_OWNER_DATA_KEY
          );
          return;
      }
}

/*
 *    Gets the DDE as a string.
 *
 *    The calling function must deallocate the returned pointer.
 */
gchar *GUIDDEGetString(
      GtkWidget *w,                 /* Widget making request */
      GdkAtom selection_atom,       /* Can be GDK_NONE */
      gulong t                /* Time */
)
{
      gchar *data;
      guint sig_id;
      GdkAtom type;
      GtkSelectionData *selection_data;
      static GdkAtom target_atom = GDK_NONE;

      if(target_atom == GDK_NONE)
          target_atom = gdk_atom_intern("STRING", FALSE);

      if(selection_atom == GDK_NONE)
          selection_atom = GDK_SELECTION_PRIMARY;

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

      if(GTK_WIDGET_NO_WINDOW(w))
      {
          g_printerr(
"GUIDDEGetString(): Failed:\
 GtkWidget 0x%.8x does not have a GdkWindow.\n",
            (guint32)w
          );
          return(NULL);
      }

      /* Set up callback data */
      selection_data = (GtkSelectionData *)g_malloc0(
          sizeof(GtkSelectionData)
      );
      selection_data->selection = selection_atom;
      selection_data->target = target_atom;
      selection_data->type = GDK_NONE;
      selection_data->format = -1;
      selection_data->data = NULL;
      selection_data->length = -1;  /* -1 means data not received */

      /* Set up widget to receive the "selection_received" signal */
      sig_id = gtk_signal_connect(
          GTK_OBJECT(w), "selection_received",
          GTK_SIGNAL_FUNC(GUIDDESelectionReceivedCB),
          selection_data
      );

      /* Request selection */
      if(!gtk_selection_convert(
          w,                  /* Widget */
          selection_atom,     /* Selection */
          target_atom,  /* Target */
          (guint32)t          /* Time */
      ))
      {
          /* Selection request failed */
          GTK_SIGNAL_DISCONNECT(GTK_OBJECT(w), sig_id)
          g_free(selection_data->data);
          g_free(selection_data);
          return(NULL);
      }

      /* Wait for selection */
      while(selection_data->length < 0)
      {
          while(gtk_events_pending() > 0)
            gtk_main_iteration();
      }
      /* Got selection */

      /* Disconnect signals */
      GTK_SIGNAL_DISCONNECT(GTK_OBJECT(w), sig_id);

      /* Get data from selection from callback data */
      type = selection_data->type;
      /* Integer */
      if(type == GDK_SELECTION_TYPE_INTEGER)
      {
          switch(selection_data->length)
          {
            case 1:
            data = g_strdup_printf(
                "%i",
                *(gint8 *)selection_data->data
            );
            break;
            case 2:
            data = g_strdup_printf(
                "%i",
                *(gint16 *)selection_data->data
            );
            break;
            case 4:
            data = g_strdup_printf(
                "%i",
                *(gint32 *)selection_data->data
            );
            break;
            case 8:
            data = g_strdup_printf(
                "%ld",
                (long)(*(gint64 *)selection_data->data)
            );
            break;
            default:
            data = NULL;
            break;
          }
      }
      /* String (latin-1 characters) */
      else if(type == GDK_SELECTION_TYPE_STRING)
      {
          data = (gchar *)selection_data->data;
          selection_data->data = NULL;
      }
      else
      {
          data = NULL;
      }

      /* Delete callback data */
      g_free(selection_data->data);
      g_free(selection_data);

      return(data);
}


/*
 *    Sets up widget as a DND drag source.
 */
void GUIDNDSetSrc(
      void *w, const void *drag_type, int ndrag_types,
      unsigned int actions, unsigned int buttons,
      void (*begin_cb)(GtkWidget *, GdkDragContext *, gpointer),
      void (*request_data_cb)(GtkWidget *, GdkDragContext *,
            GtkSelectionData *, guint, guint, gpointer),
      void (*delete_data_cb)(GtkWidget *, GdkDragContext *, gpointer),
      void (*end_cb)(GtkWidget *, GdkDragContext *, gpointer),
      gpointer client_data
)
{
      GtkWidget *widget = (GtkWidget *)w;
      const GtkTargetEntry *target_entry = (const GtkTargetEntry *)drag_type;
      gint total_target_entries = (gint)ndrag_types;

      if((widget == NULL) || (target_entry == NULL) ||
         (total_target_entries <= 0)
      )
          return;

      /* Widget must have window */
      if(GTK_WIDGET_NO_WINDOW(widget))
      {
          g_printerr(
"GUIDNDSetSrc(): Widget does not have window.\n"
          );
          return;
      }

      /* Set the widget as a DND drag source */
      gtk_drag_source_set(
          widget,
          buttons,
          target_entry, total_target_entries,
          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(widget), "drag_begin",
          GTK_SIGNAL_FUNC(GUIDNDDragBeginCB), NULL
      );
      if(begin_cb != NULL)
          gtk_signal_connect_after(
            GTK_OBJECT(widget), "drag_begin",
            GTK_SIGNAL_FUNC(begin_cb), client_data
          );

      /* Set the "drag_data_get" signal callback */
      if(request_data_cb != NULL)
          gtk_signal_connect_after(
            GTK_OBJECT(widget), "drag_data_get",
            GTK_SIGNAL_FUNC(request_data_cb), client_data
          );

      /* Set the "drag_data_delete" signal callback */
      if(delete_data_cb != NULL)
          gtk_signal_connect_after(
            GTK_OBJECT(widget), "drag_data_delete",
            GTK_SIGNAL_FUNC(delete_data_cb), client_data
          );

      /* Set the "drag_end" signal callback */
      if(end_cb != NULL)
          gtk_signal_connect_after(
            GTK_OBJECT(widget), "drag_end",
            GTK_SIGNAL_FUNC(end_cb), client_data
          );

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

/*
 *    Sets up a widget as DND drop target.
 */
void GUIDNDSetTar(
      void *w, const void *drag_type, int ndrag_types,
      unsigned int actions, unsigned int default_action_same,
      unsigned int default_action,
      void (*recieved_data_cb)(GtkWidget *, GdkDragContext *,
            gint, gint, GtkSelectionData *, guint, guint,
            gpointer     
      ),
      gpointer client_data
)
{
      static gboolean init_cb_structs = TRUE;
      GtkWidget *widget = (GtkWidget *)w;
      const GtkTargetEntry *target_entry = (const GtkTargetEntry *)drag_type;
      gint total_target_entries = (gint)ndrag_types;
      static gui_dnd_cb_struct      move_same_cb_data,
                              move_cb_data,
                              copy_cb_data;
      gui_dnd_cb_struct *cb_data_ptr = NULL;

      if((widget == NULL) || (target_entry == NULL) ||
         (total_target_entries <= 0)
      )
          return;

      /* Widget must have window */
      if(GTK_WIDGET_NO_WINDOW(widget))
      {
          g_printerr(
"GUIDNDSetTar(): Widget does not have window.\n"
          );
          return;
      }

      /* Set the widget as a DND drop target */
      gtk_drag_dest_set(
          widget,
          GTK_DEST_DEFAULT_MOTION |
          GTK_DEST_DEFAULT_HIGHLIGHT |
          GTK_DEST_DEFAULT_DROP,
          target_entry, total_target_entries,
          actions
      );

      /* Do we need to initialize DND callback data? */
      if(init_cb_structs)
      {
          move_same_cb_data.default_action_same = GDK_ACTION_MOVE;
          move_same_cb_data.default_action = GDK_ACTION_COPY;

          move_cb_data.default_action_same = GDK_ACTION_MOVE;
          move_cb_data.default_action = GDK_ACTION_MOVE;

          copy_cb_data.default_action_same = GDK_ACTION_COPY;
          copy_cb_data.default_action = GDK_ACTION_COPY;

          init_cb_structs = FALSE;
      }

      /* Set local drag motion callback data */
      /* Move only on same? */
      if((default_action_same == GDK_ACTION_MOVE) &&
         (default_action == GDK_ACTION_COPY)
      )
          cb_data_ptr = &move_same_cb_data;
      /* Always move? */
      else if((default_action_same == GDK_ACTION_MOVE) &&
            (default_action == GDK_ACTION_MOVE)
      )
          cb_data_ptr = &move_cb_data;
      /* Always copy? */
      else if((default_action_same == GDK_ACTION_COPY) &&
            (default_action == GDK_ACTION_COPY)
      )
          cb_data_ptr = &copy_cb_data;

#if 0
      /* Set this target widget to highlight when a drag goes over it */
      gtk_drag_highlight(widget);
#endif

      /* Set our local drag motion callback */
      gtk_signal_connect_after(
          GTK_OBJECT(widget), "drag_motion",
          GTK_SIGNAL_FUNC(GUIDNDDragMotionCB), cb_data_ptr
      );
      gtk_signal_connect_after(
          GTK_OBJECT(widget), "drag_drop",
          GTK_SIGNAL_FUNC(GUIDNDDragDropCB), cb_data_ptr
      );

      /* Set data received callback for local and calling functions */
      gtk_signal_connect_after(
          GTK_OBJECT(widget), "drag_data_received",
          GTK_SIGNAL_FUNC(GUIDNDDragReceivedCB), cb_data_ptr
      );
      if(recieved_data_cb != NULL)
          gtk_signal_connect_after(
            GTK_OBJECT(widget), "drag_data_received",
            GTK_SIGNAL_FUNC(recieved_data_cb), client_data
          );
}

/*
 *    Sets the DND Icon for the next DND operation with the specified
 *    pixmap and mask.
 *
 *    If pixmap is NULL then the DND Icon is destroyed/shutdown.
 */
void GUIDNDSetDragIcon(
      GdkPixmap *pixmap, GdkBitmap *mask, gint hot_x, gint hot_y
)
{
      gboolean is_pixmap_set = FALSE;
      GdkWindow *window;
      GtkWidget *w;
      gui_dnd_icon_struct *dnd_icon = &gui_dnd_icon;

      /* Destroy the DND Icon? */
      if(pixmap == NULL)
      {
          w = dnd_icon->toplevel; 
          if(w != NULL)
          {
            GTK_WIDGET_DESTROY(dnd_icon->icon_pm)
            GTK_WIDGET_DESTROY(dnd_icon->toplevel)
            memset(dnd_icon, 0x00, sizeof(gui_dnd_icon_struct));
          }
          return;
      }

      /* Check if the DND Icon needs to be initialized for the very
       * first time
       */
      w = dnd_icon->toplevel;
      if(w == NULL)
      {
          GtkWidget *parent;

          dnd_icon->toplevel = w = gtk_window_new(GTK_WINDOW_POPUP);
          gtk_widget_realize(w);
          parent = w;

          dnd_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 GdkWindow of the DND Icon toplevel */
      w = dnd_icon->toplevel;
      window = (w != NULL) ? w->window : NULL;
      if(window == NULL)
          return;

      /* Begin setting up the DND Icon with the new values */
      dnd_icon->x = hot_x;
      dnd_icon->y = hot_y;

      gdk_window_get_size(
          pixmap, &dnd_icon->width, &dnd_icon->height
      );

      /* Adjust size and shape of the DND Icon's toplevel window */
      gtk_widget_set_usize(w, dnd_icon->width, dnd_icon->height);
      gdk_window_set_back_pixmap(window, pixmap, 0);
      gtk_widget_shape_combine_mask(w, mask, 0, 0);

      /* Set up the icon GtkPixmap */
      w = dnd_icon->icon_pm;
      if((w != NULL) && !is_pixmap_set)
      {
          gtk_pixmap_set(GTK_PIXMAP(w), pixmap, mask);
      }
}

/*
 *    Creates a new Banner.
 */
GtkWidget *GUIBannerCreate(
      const gchar *label,
      const gchar *font_name,
      GdkColor color_fg,
      GdkColor color_bg,
      GtkJustification justify,           /* One of GTK_JUSTIFY_* */
      gboolean expand
)
{
      GdkFont *font = NULL;
      GtkRcStyle *rcstyle;
      GtkWidget *w = gtk_drawing_area_new();
      gpointer *cb_data = (gpointer *)g_malloc0(
          4 * sizeof(gpointer)
      );

      cb_data[0] = STRDUP(label);
      cb_data[1] = (gpointer)justify;
      cb_data[2] = font = (font_name != NULL) ?
          gdk_font_load(font_name) : NULL;
      cb_data[3] = NULL;
      gtk_object_set_data_full(
          GTK_OBJECT(w), GUI_BANNER_DATA_KEY,
          cb_data, GUIBannerDataDestroyCB
      );

      gtk_widget_add_events(
          w,
          GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "configure_event",
          GTK_SIGNAL_FUNC(GUIBannerEventCB), cb_data
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "expose_event",
          GTK_SIGNAL_FUNC(GUIBannerEventCB), cb_data
      );

      /* Set style */
      rcstyle = gtk_rc_style_new();
      if(!STRISEMPTY(font_name))
          rcstyle->font_name = STRDUP(font_name);
      rcstyle->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG | GTK_RC_BG;
      rcstyle->color_flags[GTK_STATE_INSENSITIVE] = GTK_RC_FG | GTK_RC_BG;
      memcpy(&rcstyle->fg[GTK_STATE_NORMAL], &color_fg, sizeof(GdkColor));
      memcpy(&rcstyle->bg[GTK_STATE_NORMAL], &color_bg, sizeof(GdkColor));
      /* Modify foreground and background colors for insensitive */
#define LOWER_VALUE(c)  (gushort)((((gint)(c) - (gint)((guint16)-1 / 2)) * 0.75) + \
(gint)((guint16)-1 / 2))
      color_fg.red = LOWER_VALUE(color_fg.red);
      color_fg.green = LOWER_VALUE(color_fg.green);
      color_fg.blue = LOWER_VALUE(color_fg.blue);
      color_bg.red = LOWER_VALUE(color_bg.red);
      color_bg.green = LOWER_VALUE(color_bg.green);
      color_bg.blue = LOWER_VALUE(color_bg.blue);
#undef LOWER_VALUE
      memcpy(&rcstyle->fg[GTK_STATE_INSENSITIVE], &color_fg, sizeof(GdkColor));
      memcpy(&rcstyle->bg[GTK_STATE_INSENSITIVE], &color_bg, sizeof(GdkColor));

      gtk_widget_modify_style(w, rcstyle);

      GTK_RC_STYLE_UNREF(rcstyle);


      /* Get new style values from widget */
      if(font != NULL)
      {
          gint width, height;

          if(expand)
            width = -1;
          else if((font != NULL) && (label != NULL))
            width = gdk_string_width(font, label);
          else
            width = -1;

          if(font != NULL)
            height = font->ascent + font->descent;
          else
            height = -1;
  
          gtk_widget_set_usize(w, width, height);
      }

      return(w);
}

/*
 *    Draws the Banner.
 */
void GUIBannerDraw(GtkWidget *w)
{
      GtkStateType state;
      GtkJustification justify;
      gint width, height;
      const gchar *label;
      GdkFont *font;
      GdkWindow *window;
      GdkDrawable *drawable;
      GtkStyle *style;
      gpointer *cb_data = (gpointer *)((w != NULL) ?
          gtk_object_get_data(GTK_OBJECT(w), GUI_BANNER_DATA_KEY) : NULL
      );
      if(cb_data == NULL)
          return;

      label = (const gchar *)cb_data[0];
      justify = (GtkJustification)cb_data[1];
      font = (GdkFont *)cb_data[2];

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

      drawable = (GdkDrawable *)window;

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

      /* Begin drawing */

      /* Background */
      gdk_draw_rectangle(
          drawable, style->bg_gc[state], TRUE,
          0, 0, width, height
      );

      /* Got font and label? */
      if((font != NULL) && !STRISEMPTY(label))
      {
          const gint border = 3;
          gint lbearing, rbearing, swidth, ascent, descent;

          gdk_string_extents(
            font, label,
            &lbearing, &rbearing, &swidth, &ascent, &descent
          );

          switch(justify)
          {
            case GTK_JUSTIFY_CENTER:
            gdk_draw_string(
                drawable, font, style->fg_gc[state],
                ((width - swidth) / 2) - lbearing,
                ((height - (font->ascent + font->descent)) / 2) +
                  font->ascent,
                label  
            );
            break;
            case GTK_JUSTIFY_RIGHT:
            gdk_draw_string(
                drawable, font, style->fg_gc[state],
                (width - swidth - border) - lbearing,
                ((height - (font->ascent + font->descent)) / 2) +
                  font->ascent,
                label
            );
            break;
            case GTK_JUSTIFY_LEFT:
            gdk_draw_string(
                drawable, font, style->fg_gc[state],
                border - lbearing,
                ((height - (font->ascent + font->descent)) / 2) +
                  font->ascent,
                label                    
            );
            break;
            case GTK_JUSTIFY_FILL:
            break;
          }
      }

      /* Send drawable to window if drawable is not the window */
      if(drawable != window)
          gdk_draw_pixmap(
            window, style->fg_gc[state], 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(
          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(
          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 = (gpointer *)GTK_OBJECT_GET_DATA(
          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 = (GtkCombo *)w;
      gpointer *cb_data = (gpointer *)GTK_OBJECT_GET_DATA(
          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.
 *
 *    If functional_widget_rtn not NULL then it will refer to the
 *    functional widget of the new Menu Item. For example, if the type
 *    was GUI_MENU_ITEM_TYPE_LABEL then *functional_widget_rtn will
 *    refer to the GtkLabel.
 */
GtkWidget *GUIMenuItemCreate(
      GtkWidget *menu,
      gui_menu_item_type type,      /* One of GUI_MENU_ITEM_TYPE_* */
      GtkAccelGroup *accelgrp,
      guint8 **icon, const gchar *label,
      guint accel_key, guint accel_mods,
      gpointer *functional_widget_rtn,
      gpointer data,
      void (*func_cb)(GtkWidget *w, gpointer)
)
{
      const gint  border_minor = 2,
                  border_accel_label = 15;
      GtkWidget *w, *menu_item = NULL, *parent, *parent2;
      gui_menu_item_data_struct *mi;

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

      /* Reset functional widget ID */
      if(functional_widget_rtn != NULL)
          *functional_widget_rtn = NULL;

      /* Create menu item data */
      mi = GUI_MENU_ITEM_DATA(
          g_malloc0(sizeof(gui_menu_item_data_struct))
      );

      /* Create menu item by type */
      switch(type)
      {
        case GUI_MENU_ITEM_TYPE_LABEL:
        case GUI_MENU_ITEM_TYPE_SUBMENU:
          /* Create a GtkMenuItem */
          mi->menu_item = menu_item = parent = w = gtk_menu_item_new();
          if(!GTK_WIDGET_NO_WINDOW(w))
            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, GUIMenuDataDestroyCB
          );
          gtk_widget_show(w);
          /* Create a GtkTable to hold the icon and labels */
          w = gtk_table_new(1, 3, FALSE);
          gtk_container_add(GTK_CONTAINER(parent), w);
#ifndef HAVE_WIN32
          gtk_table_set_col_spacing(
            GTK_TABLE(w), 0, (guint)border_minor
          );
          gtk_table_set_col_spacing(
            GTK_TABLE(w), 1,
            (guint)border_accel_label
          );
#endif
          gtk_widget_show(w);
          parent = w;

          /* Create icon, if 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,
                0, 1,
                0, 1,
                GTK_SHRINK,
                GTK_SHRINK,
                0, 0
            );
            gtk_widget_show(w);
          }

          /* Create a GtkAlignment to align the label */
          w = gtk_alignment_new(0.0f, 0.5f, 0.0f, 0.0f);
          gtk_table_attach(
            GTK_TABLE(parent), w,
            1, 2,
            0, 1,
            GTK_SHRINK | GTK_EXPAND | GTK_FILL,
            GTK_SHRINK,
            0, 0
          );
          gtk_widget_show(w);
          parent2 = w;
          /* Create a GtkLabel as the label */
          mi->label_text = STRDUP((label != NULL) ? label : "");
          mi->label = w = gtk_label_new(mi->label_text);
          gtk_container_add(GTK_CONTAINER(parent2), w);
          gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
          gtk_widget_show(w);
          /* Record the GtkLabel as the functional widget */
          if(functional_widget_rtn != NULL)
              *functional_widget_rtn = w;

          /* Set callback for menu item? */
          if(func_cb != NULL)
          {
            /* Create menu item client data */
            gpointer *mi_client_data = (gpointer *)g_malloc0(
                4 * sizeof(gpointer)
            );
            mi_client_data[0] = w;  /* Functional widget (GtkLabel) */
            mi_client_data[1] = data;
            mi_client_data[2] = (gpointer)func_cb;
            mi_client_data[3] = NULL;
            gtk_object_set_data_full(
                GTK_OBJECT(menu_item), GUI_MENU_ITEM_CLIENT_DATA_KEY,
                mi_client_data, GUIMenuClientDataDestroyCB
            );

            gtk_signal_connect_object(
                GTK_OBJECT(menu_item), "activate",
                GTK_SIGNAL_FUNC(GUIMenuActivateCB), (gpointer)mi_client_data
            );
          }

          /* Create a GtkAlignment for the accelerator key label */
          w = gtk_alignment_new(1.0f, 0.5f, 0.0f, 0.0f);
          gtk_table_attach(
            GTK_TABLE(parent), w,
            2, 3,
            0, 1,
            GTK_SHRINK,
            GTK_SHRINK,
            0, 0
          );
          gtk_widget_show(w);
          parent2 = w;
          /* Create a GtkLabel as the accelerator key label */
          mi->accel_label = w = gtk_label_new("");
          gtk_container_add(GTK_CONTAINER(parent2), w);
          gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT);

          /* Set accelerator key and accelerator key GtkLabel */
          GUIMenuItemSetAccelKey(
            menu_item, accelgrp, accel_key, accel_mods
          );
          break;

        case GUI_MENU_ITEM_TYPE_CHECK:
          /* Create a GtkCheckMenuItem */
          mi->label_text = STRDUP((label != NULL) ? label : "");
          mi->menu_item = menu_item = parent = w =
            gtk_check_menu_item_new_with_label(mi->label_text);
          /* Need to set default height */
          gtk_widget_set_usize(w, -1, GUI_MENU_ITEM_DEF_HEIGHT + 2);
          gtk_menu_append(GTK_MENU(menu), w);
          gtk_object_set_data_full(
            GTK_OBJECT(w), GUI_MENU_ITEM_DATA_KEY,
            mi, GUIMenuDataDestroyCB
          );
          gtk_widget_show(w);

          /* Record check menu item as the functional widget */
          if(functional_widget_rtn != NULL)
            *functional_widget_rtn = w;

          /* Set callback for the check menu item? */
          if(func_cb != NULL)
          {
            /* Create menu item client data */
            gpointer *mi_client_data = (gpointer *)g_malloc0(
                4 * sizeof(gpointer)
            );
            mi_client_data[0] = w;  /* Functional widget */
            mi_client_data[1] = data;
            mi_client_data[2] = (gpointer)func_cb;
            mi_client_data[3] = NULL;
            gtk_object_set_data_full(
                GTK_OBJECT(menu_item), GUI_MENU_ITEM_CLIENT_DATA_KEY,
                mi_client_data, GUIMenuClientDataDestroyCB
            );

            gtk_signal_connect_object(
                GTK_OBJECT(menu_item), "toggled",
                GTK_SIGNAL_FUNC(GUIMenuActivateCB), (gpointer)mi_client_data
            );
          }

          /* Set accelerator key */
          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, GUIMenuDataDestroyCB
          );
          gtk_widget_show(w);

          /* Create horizontal separator */
          w = gtk_hseparator_new();
          gtk_container_add(GTK_CONTAINER(parent), w);
          GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(menu_item), GTK_SENSITIVE);
          gtk_widget_show(w);

          /* Record horizontal separator as the functional widget */
          if(functional_widget_rtn != NULL)
            *functional_widget_rtn = w;
          break;

        default:
          GUIMenuDataDestroyCB(mi);
          mi = NULL;
          break;
      }


      return(menu_item);
}

/*
 *    Sets the Menu Item as the default item.
 */
void GUISetMenuItemDefault(GtkWidget *w)
{
#if 0
      GtkRcStyle *rcstyle;
#endif
      gui_menu_item_data_struct *cb_data = GUI_MENU_ITEM_DATA(
          GTK_OBJECT_GET_DATA(w, GUI_MENU_ITEM_DATA_KEY)
      );
      if(cb_data == NULL)
          return;

#if 0
      rcstyle = gtk_rc_style_new();
      rcstyle->font_name = STRDUP(
          "-*-*-bold-*-*-*-*-*-*-*-*-*-*-*"
/* "-adobe-helvetica-bold-r-normal-*-12-*-*-*-*-*-iso8859-1" */
      );
      w = cb_data->label;
      if(w != NULL)
          gtk_widget_modify_style(w, rcstyle);
      w = cb_data->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((GtkWidget *)w), "enter_notify_event",
            GTK_SIGNAL_FUNC(enter_cb), enter_data
          );
      if(leave_cb != NULL)
          gtk_signal_connect(
            GTK_OBJECT((GtkWidget *)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,
      gui_menu_bar_item_alignment align   /* One of GUI_MENU_BAR_ALIGN_* */
)
{
      GtkWidget *menu_label;
      GtkMenuShell *menu_shell;

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

      menu_shell = GTK_MENU_SHELL(menu_bar);

      /* Create Menu Label */
      if(label != NULL)
      {
          menu_label = gtk_menu_item_new_with_label(label);
      }
      else
      {
          GList *glist = menu_shell->children;
          gchar *s = g_strdup_printf(
            "Menu %i", g_list_length(glist) + 1
          );
          menu_label = gtk_menu_item_new_with_label(s);
          g_free(s);
      }

      /* Set Menu Label to reffer to the given Menu */
      gtk_menu_item_set_submenu(
          GTK_MENU_ITEM(menu_label), menu
      );

      /* Append Menu Label to the given Menu Bar */
      gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_label);
      gtk_widget_show(menu_label);

      /* Set Menu Label alignment */
      if(align == GUI_MENU_BAR_ALIGN_RIGHT)
          gtk_menu_item_right_justify(GTK_MENU_ITEM(menu_label));

      return(menu_label);
}

/*
 *    Sets the Menu Item's label.
 */
void GUIMenuItemSetLabel(GtkWidget *menu_item, const gchar *label)
{
      GtkWidget *w;
      gui_menu_item_data_struct *mi = GUI_MENU_ITEM_DATA(
          GTK_OBJECT_GET_DATA(menu_item, GUI_MENU_ITEM_DATA_KEY)
      );
      if(mi == NULL)
          return;

      w = mi->label;

      if(label != NULL)
      {
          /* No change in label text? */
          const gchar *old_label = mi->label_text;
          if((old_label != NULL) ? !strcmp(old_label, label) : FALSE)
            return;

          /* Record new label text */
          g_free(mi->label_text);
          mi->label_text = STRDUP(label);

          /* Set new label text */
          if((w != NULL) ? GTK_IS_LABEL(w) : FALSE)
            gtk_label_set_text(GTK_LABEL(w), mi->label_text);
      }
      else
      {
          /* Label text was already empty? */
          if(mi->label_text == NULL)
            return;

          /* Record new label text */
          g_free(mi->label_text);
          mi->label_text = STRDUP("(null)");
    
          /* Set new label text */
          if((w != NULL) ? GTK_IS_LABEL(w) : FALSE)
            gtk_label_set_text(GTK_LABEL(w), mi->label_text);
      }
}

/*
 *    Sets the Menu Item's pixmap.
 */
void GUIMenuItemSetPixmap(GtkWidget *menu_item, guint8 **icon_data)
{
      GdkWindow *window;
      GtkStyle *style;
      GtkWidget *w;
      gui_menu_item_data_struct *mi = GUI_MENU_ITEM_DATA(
          GTK_OBJECT_GET_DATA(menu_item, GUI_MENU_ITEM_DATA_KEY)
      );
      if((mi == NULL) || (icon_data == NULL))
          return;

      if(gui_window_root == NULL)
           gui_window_root = (GdkWindow *)GDK_ROOT_PARENT();
      window = gui_window_root;

      /* No change in icon data? */
      if(mi->icon_data == (const guint8 **)icon_data)
          return;

      style = gtk_widget_get_style(menu_item);
      w = mi->pixmap;
      if((w != NULL) ? GTK_IS_PIXMAP(w) : FALSE)
      {
          GdkBitmap *mask = NULL;
          GdkPixmap *pixmap = gdk_pixmap_create_from_xpm_d(
            window, &mask,
            (style != NULL) ?
                &style->bg[GTK_STATE_NORMAL] : NULL,
            (gchar **)icon_data
          );
          if(pixmap != NULL)
          {
            gint width, height;

            /* Record new icon data */
            mi->icon_data = (const guint8 **)icon_data;

            /* Update pixmap */
            gtk_pixmap_set(GTK_PIXMAP(w), pixmap, mask);

            /* Get size of newly loaded pixmap */
            gdk_window_get_size(pixmap, &width, &height);
          }
          GDK_PIXMAP_UNREF(pixmap);
          GDK_BITMAP_UNREF(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,
      guint accel_key, guint accel_mods
)
{
      GtkWidget *w;
      gui_menu_item_data_struct *mi = GUI_MENU_ITEM_DATA(
          GTK_OBJECT_GET_DATA(menu_item, GUI_MENU_ITEM_DATA_KEY)
      );
      if(mi == NULL)
          return;

      /* No change? */
      if((mi->accel_key == accel_key) &&
         (mi->accel_mods == accel_mods)
      )
          return;
 
      /* Update the GtkAccelGroup? */
      if(accelgrp != NULL)
      {
          /* Remove existing accelerator key? */
          if(mi->accel_key != 0)
            gtk_widget_remove_accelerator(
                menu_item, accelgrp,
                mi->accel_key, mi->accel_mods
            );
          /* Add new accelerator key */
          gtk_widget_add_accelerator(
            menu_item, "activate", accelgrp,
            accel_key, accel_mods,
            GTK_ACCEL_VISIBLE
          );
      }

      /* Update the accelerator key GtkLabel */
      w = mi->accel_label;
      if(w != NULL)
      {
          GtkLabel *label = GTK_LABEL(w);

          /* Accelerator key specified? */
          if(accel_key != 0)
          {
            const gchar *key_name;
            gchar *accel_text = 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(mi->accel_text);
            mi->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(mi->accel_text);
            mi->accel_text = NULL;
            gtk_label_set_text(label, "");
            gtk_widget_hide(w);
          }
      }

      /* Record new accelerator key value */
      mi->accel_key = accel_key;
      mi->accel_mods = accel_mods;
}

/*
 *    Sets the Check Menu Item's active state.
 */
void GUIMenuItemSetCheck(
      GtkWidget *menu_item, gboolean active, gboolean emit
)
{
#if 1
      if((menu_item != NULL) ? !GTK_IS_CHECK_MENU_ITEM(menu_item) : TRUE)
          return;

      if(emit)
          gtk_check_menu_item_set_active(
            GTK_CHECK_MENU_ITEM(menu_item), active
          );
      else
          GTK_CHECK_MENU_ITEM(menu_item)->active = active;
#else
      GtkWidget *w;
      gui_menu_item_data_struct *mi = GUI_MENU_ITEM_DATA(
          GTK_OBJECT_GET_DATA(menu_item, GUI_MENU_ITEM_DATA_KEY)
      );
      if(mi == NULL)
          return;

      w = mi->check;

#endif
}

/*
 *    Gets the Check Menu Item's active state.
 */
gboolean GUIMenuItemGetCheck(GtkWidget *menu_item)
{
#if 1
      if(menu_item == NULL)
          return(FALSE);

      if(GTK_IS_CHECK_MENU_ITEM(menu_item))
          return(GTK_CHECK_MENU_ITEM(menu_item)->active);
      else
          return(FALSE);
#else






#endif
}


/*
 *    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,
      gui_menu_bar_item_alignment align
)
{
      return(GUIMenuAddToMenuBar(
          menu_bar, menu, label, align
      ));
}

/*
 *    Links the Menu Item to the Sub Menu.
 */
void GUIMenuItemSetSubMenu(
      GtkWidget *menu_item, GtkWidget *sub_menu
)
{
      if(menu_item == NULL)
          return;

      if(sub_menu == NULL)
          gtk_menu_item_remove_submenu(GTK_MENU_ITEM(menu_item));
      else
          gtk_menu_item_set_submenu(
            GTK_MENU_ITEM(menu_item), sub_menu
          );
}


/*
 *    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(
          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(
          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(
          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
            );
          }
      }
}

Generated by  Doxygen 1.6.0   Back to index