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

pulist.c

#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "guiutils.h"
#include "pulist.h"


/* Popup List Callbacks */
static gint PUListDeleteEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
);
static gint PUListConfigureEventCB(
      GtkWidget *widget, GdkEventConfigure *configure, gpointer data
);
static gint PUListKeyPressEventCB(
      GtkWidget *widget, GdkEventKey *key, gpointer data
);
static gint PUListButtonPressEventCB(
      GtkWidget *widget, GdkEventButton *button, gpointer data
);
static gint PUListMotionNotifyEventCB(
      GtkWidget *widget, GdkEventMotion *motion, gpointer data
);

static gint PUListShadowPaintCB(pulist_struct *list);
static void PUListShadowDraw(pulist_struct *list);
static void PUListShadowConfigure(
      pulist_struct *list, gint x, gint y, gint width, gint height
);

static void PUListCListDoDragSetUp(pulist_struct *list);
static void PUListCListDoDragCleanUp(pulist_struct *list);


/* Map Button Callbacks */
static gint PUListMapButtonExposeCB(
      GtkWidget *widget, GdkEventExpose *expose, gpointer data
);


/* Popup List Box Callbacks */
static gint PUListBoxEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
);
static void PUListBoxMapCB(GtkWidget *widget, gpointer data);
static void PUListBoxDraw(pulistbox_struct *box);


/* Popup List */
gint PUListFindItemFromValue(pulist_struct *list, const gchar *value);
gpointer PUListGetDataFromValue(
      pulist_struct *list, const gchar *value
);
GtkWidget *PUListGetToplevel(pulist_struct *list);
GtkWidget *PUListGetCList(pulist_struct *list);

gint PUListAddItem(
      pulist_struct *list, const gchar *value
);
gint PUListAddItemPixText(
      pulist_struct *list, const gchar *value,
      GdkPixmap *pixmap, GdkBitmap *mask
);
void PUListClear(pulist_struct *list);

void PUListSetItemText(
      pulist_struct *list, gint i,
      const gchar *value
);
void PUListSetItemPixText(
      pulist_struct *list, gint i,
      const gchar *value,
      GdkPixmap *pixmap, GdkBitmap *mask
);
void PUListSetItemData(
      pulist_struct *list, gint i,
      gpointer data
);
void PUListSetItemDataFull(
      pulist_struct *list, gint i,
      gpointer data, GtkDestroyNotify destroy_cb
);

void PUListGetItemText(
      pulist_struct *list, gint i,
      gchar **value 
);
void PUListGetItemPixText(
      pulist_struct *list, gint i,
      gchar **value,
      GdkPixmap **pixmap, GdkBitmap **mask 
);
gpointer PUListGetItemData(pulist_struct *list, gint i);

gint PUListGetSelectedLast(pulist_struct *list);
void PUListSelect(pulist_struct *list, gint i);
void PUListUnselectAll(pulist_struct *list);

gboolean PUListIsQuery(pulist_struct *list);
void PUListBreakQuery(pulist_struct *list);
const gchar *PUListMapQuery(
      pulist_struct *list,
      const gchar *value,           /* Initial value */
      gint lines_visible,           /* Can be -1 for default */
      pulist_relative relative,     /* One of PULIST_RELATIVE_* */
      GtkWidget *rel_widget,        /* Map relative to this widget */
      GtkWidget *map_widget         /* Widget that mapped this list */
);

pulist_struct *PUListNew(void);
void PUListDelete(pulist_struct *list);


/* Map Button */
GtkWidget *PUListNewMapButton(
      void (*map_cb)(GtkWidget *, gpointer),
      gpointer client_data
);
GtkWidget *PUListNewMapButtonArrow(
      GtkArrowType arrow_type, GtkShadowType shadow_type,
      void (*map_cb)(GtkWidget *, gpointer),
      gpointer client_data
);


/* Popup List Box */
GtkWidget *PUListBoxGetToplevel(pulistbox_struct *box);
pulist_struct *PUListBoxGetPUList(pulistbox_struct *box);

pulistbox_struct *PUListBoxNew(
      GtkWidget *parent,
      gint width, gint height
);
void PUListBoxSetChangedCB(
      pulistbox_struct *box,
      void (*func)(
            pulistbox_struct *,     /* Popup List Box */
            gint,             /* Item */
            gpointer          /* Data */
      ),
      gpointer data
);
void PUListBoxMap(pulistbox_struct *box);
void PUListBoxUnmap(pulistbox_struct *box);
void PUListBoxDelete(pulistbox_struct *box);


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


#define POPUP_LIST_DEF_WIDTH        320
#define POPUP_LIST_DEF_HEIGHT       200

#define POPUP_LIST_ROW_SPACING            20

#define POPUP_LIST_MAP_BTN_WIDTH    17
#define POPUP_LIST_MAP_BTN_HEIGHT   17

#define POPUP_LIST_SHADOW_OFFSET_X         5 
#define POPUP_LIST_SHADOW_OFFSET_Y         5


/* Timeout interval in milliseconds, this is effectivly the scrolling
 * interval of the clist when button is first pressed to map the popup
 * list and then dragged over the clist without releaseing the button.
 */
#define POPUP_LIST_TIMEOUT_INT            80


/*
 *    Popup List toplevel GtkWindow "delete_event" signal callback.
 */
static gint PUListDeleteEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      pulist_struct *list = (pulist_struct *)data;
      if(list == NULL)
          return(FALSE);

      while(list->gtk_block_level > 0)
      {
          list->gtk_block_level--;
          gtk_main_quit();
      }

      return(TRUE);
}

/*
 *    Popup List toplevel GtkWindow "configure_event" signal callback.
 */
static gint PUListConfigureEventCB(
      GtkWidget *widget, GdkEventConfigure *configure, gpointer data
)
{
      pulist_struct *list = (pulist_struct *)data;
      if((configure == NULL) || (list == NULL))
          return(FALSE);

      /* Update shadow geometry */
      PUListShadowConfigure(
          list,
          configure->x, configure->y,
          configure->width, configure->height
      );

      return(TRUE);
}

/*
 *    Popup List "key_press_event" and "key_release_event" signal
 *      callback.
 */
static gint PUListKeyPressEventCB(
      GtkWidget *widget, GdkEventKey *key, gpointer data
)
{
      gint status = FALSE;
      gint etype;
      gboolean press;
      guint keyval, state;
      pulist_struct *list = (pulist_struct *)data;
      if((widget == NULL) || (key == NULL) || (list == NULL))
          return(status);

#define DO_BREAK_GTK_MAIN_LOOP      {     \
 while(list->gtk_block_level > 0) { \
  list->gtk_block_level--;          \
  gtk_main_quit();                  \
 }                            \
}

#define DO_ADJ_CLAMP_EMIT     {           \
 if(adj->value > (adj->upper - adj->page_size)) \
  adj->value = adj->upper - adj->page_size;     \
 if(adj->value < adj->lower)              \
  adj->value = adj->lower;                \
 gtk_signal_emit_by_name(                 \
  GTK_OBJECT(adj), "value_changed"        \
 );                                 \
}

/* Emits a signal stop for the key event */
#define DO_STOP_KEY_SIGNAL_EMIT     {           \
 gtk_signal_emit_stop_by_name(                  \
  GTK_OBJECT(widget),                     \
  press ?                           \
   "key_press_event" : "key_release_event"      \
 );                                 \
}

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

      /* Note: Only "key_press_events" seem to be reported and not
       * "key_release_events", so always check if press is TRUE
       */
      if(widget == list->clist)
      {
          GtkCList *clist = GTK_CLIST(widget);

          /* Handle by key value */
          switch(keyval)
          {
            case GDK_space:
            case GDK_Return:
            case GDK_KP_Enter:
            case GDK_ISO_Enter:
            case GDK_3270_Enter:
            if(press)
            {
                /* Activate selected item */
                GList *glist = clist->selection_end;
                gint row = (glist != NULL) ? (gint)glist->data : -1;
                if((row >= 0) && (row < clist->rows))
                {
                  gchar *text = NULL;
                  guint8 spacing;
                  GdkPixmap *pixmap;
                  GdkBitmap *mask;

                  switch(gtk_clist_get_cell_type(clist, row, 0))
                  {
                    case GTK_CELL_TEXT:
                      gtk_clist_get_text(clist, row, 0, &text);
                      break;
                    case GTK_CELL_PIXTEXT:
                      gtk_clist_get_pixtext(
                        clist, row, 0, &text,
                        &spacing, &pixmap, &mask
                      );
                      break;
                    case GTK_CELL_PIXMAP:
                    case GTK_CELL_WIDGET:
                    case GTK_CELL_EMPTY:
                      break;
                  }
                  if(!STRISEMPTY(text))
                  {
                      g_free(list->last_value);
                      list->last_value = STRDUP(text);
                  }
                }
                /* Break query */
                DO_BREAK_GTK_MAIN_LOOP
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;

            case GDK_Escape:
            if(press)
            {
                /* Break query */
                DO_BREAK_GTK_MAIN_LOOP
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;
#if 0
            case GDK_Page_Up:
            case GDK_KP_Page_Up:
            if(press)
            {
                /* Get adjustment and scroll up one page */
                GtkAdjustment *adj = clist->vadjustment;
                if(adj != NULL)
                {
                  adj->value -= adj->page_increment;
                  DO_ADJ_CLAMP_EMIT
                }
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;

            case GDK_Page_Down:
            case GDK_KP_Page_Down:
            if(press)
            {
                /* Get adjustment and scroll down one page */
                GtkAdjustment *adj = clist->vadjustment;
                if((adj != NULL) && press)
                {
                  adj->value += adj->page_increment;
                  DO_ADJ_CLAMP_EMIT
                }
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;

            case GDK_Home:
            case GDK_KP_Home:
            if(press)
            {
                /* Get adjustment and scroll all the way up */
                GtkAdjustment *adj = clist->vadjustment;
                if(adj != NULL)
                {
                  adj->value = adj->lower;
                  DO_ADJ_CLAMP_EMIT
                }
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;

            case GDK_End:
            case GDK_KP_End:
            if(press)
            {
                /* Get adjustment and scroll all the way up */
                GtkAdjustment *adj = clist->vadjustment;
                if(adj != NULL)
                {
                  adj->value = adj->upper - adj->page_size;
                  DO_ADJ_CLAMP_EMIT
                }
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;
#endif
          }
      }

      return(status);

#undef DO_BREAK_GTK_MAIN_LOOP
#undef DO_ADJ_CLAMP_EMIT
#undef DO_STOP_KEY_SIGNAL_EMIT
}

/*
 *    Popup List "button_press_event" or "button_release_event"
 *    signal callback.
 */
static gint PUListButtonPressEventCB(
      GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
      gint status = FALSE;
      pulist_struct *list = PULIST(data);
      if((widget == NULL) || (button == NULL) || (list == NULL))
          return(status);

#define DO_BREAK_GTK_MAIN_LOOP      {     \
 while(list->gtk_block_level > 0) { \
  list->gtk_block_level--;          \
  gtk_main_quit();                  \
} }

#define DO_RESTORE_MAP_WIDGET {     \
 GtkWidget *w = list->map_widget;   \
 if(w != NULL) {              \
  /* Handle restoring by widget type */   \
  if(GTK_IS_BUTTON(w)) {            \
   /* It is a button, make it go into     \
    * its released state            \
    */                              \
   GtkButton *button = GTK_BUTTON(w);     \
   button->in_button = 1;           \
   button->button_down = 1;         \
   gtk_signal_emit_by_name(GTK_OBJECT(w), "released"); \
   gtk_signal_emit_by_name(GTK_OBJECT(w), "leave"); \
  }                           \
 }                            \
}

      /* See which widget this event is for */
      if(widget == list->clist)
      {
          gint x, y;
          GtkCList *clist = GTK_CLIST(widget);

          /* Get the widget the event occured over */
          GtkWidget *w = gtk_get_event_widget((GdkEvent *)button);

          switch((gint)button->type)
          {
            case GDK_BUTTON_PRESS:
            x = (gint)button->x;
            y = (gint)button->y;
            /* Button pressed outside of the clist? */
            if(w != widget)
            {
                /* Clicked on one of the other GtkWidgets
                 * belonging to the Popup List?
                 */
                if((w == list->vscrollbar) ||
                   (w == list->hscrollbar) ||
                   (w == list->scrolled_window) ||
                   (w == list->toplevel)
                )
                {
                  /* Forward event to that GtkWidget */
                  gtk_widget_event(w, (GdkEvent *)button);
                }
                else
                {
                  /* All else assume pressed in some other widget
                   *
                   * Break out of the block loop and restore the
                   * map widget
                   */
                  DO_BREAK_GTK_MAIN_LOOP
                  DO_RESTORE_MAP_WIDGET
                }
            }
            /* If button 1 was pressed then mark the initial button
             * press as sent (regardless of if this event is
             * synthetic or not)
             */
            if(button->button == 1)
            {
                if(!list->initial_list_button_press_sent)
                  list->initial_list_button_press_sent = TRUE;
            }
            status = TRUE;
            break;

            case GDK_BUTTON_RELEASE:
            x = (gint)button->x;
            y = (gint)button->y;
            switch(button->button)
            {
              case 1:
                /* Button released inside the clist? */
                if((w == widget) &&
                   (x >= 0) && (x < widget->allocation.width) &&
                   (y >= 0) && (y < widget->allocation.height)
                )
                {
                  /* Button was released inside the clist,
                   * meaning we now have a matched item
                   */
                  GList *glist = clist->selection_end;
                  gint row = (glist != NULL) ? (gint)glist->data : -1;
                  if((row >= 0) && (row < clist->rows))
                  {
                      gchar *text = NULL;
                      guint8 spacing;
                      GdkPixmap *pixmap;
                      GdkBitmap *mask;
                      switch(gtk_clist_get_cell_type(clist, row, 0))
                      {
                        case GTK_CELL_TEXT:
                        gtk_clist_get_text(clist, row, 0, &text);
                          break;
                        case GTK_CELL_PIXTEXT:
                          gtk_clist_get_pixtext(
                            clist, row, 0, &text,
                            &spacing, &pixmap, &mask
                          );
                          break;
                        case GTK_CELL_PIXMAP:
                        case GTK_CELL_WIDGET:
                        case GTK_CELL_EMPTY:
                          break;
                      }
                      /* If we got a value then update the
                       * last recorded value on the Popup List
                       */
                      if(!STRISEMPTY(text))
                      {
                        g_free(list->last_value);
                        list->last_value = STRDUP(text);
                      }
                    }

                  /* Break query */
                  DO_BREAK_GTK_MAIN_LOOP
                }
                else
                {
                  /* Button was released outside of the clist */

                  /* Released over one of the other GtkWidgets
                   * belonging to the Popup List?
                   */
                  if((w == list->vscrollbar) ||
                     (w == list->hscrollbar) ||
                     (w == list->scrolled_window) ||
                     (w == list->toplevel)
                  )
                  {
                      /* Forward event to that GtkWidget */
                      gtk_widget_event(w, (GdkEvent *)button);
                  }

                  /* Grab the clist again, since releasing the
                   * button outside the clist would have
                   * ungrabbed it and not not unmap it
                   */
                  if(widget != gtk_grab_get_current())
                      gtk_grab_add(widget);
                }
                break;
            }
            /* Restore the map widget on any button release */
            DO_RESTORE_MAP_WIDGET
            status = TRUE;
            break;
          }
      }

      return(status);

#undef DO_RESTORE_MAP_WIDGET
#undef DO_BREAK_GTK_MAIN_LOOP
}

/*
 *    Popup List "motion_notify_event" signal callback.
 */
static gint PUListMotionNotifyEventCB(
      GtkWidget *widget, GdkEventMotion *motion, gpointer data
)
{
      gint status = FALSE;
      pulist_struct *list = (pulist_struct *)data;
      if((widget == NULL) || (motion == NULL) || (list == NULL))
          return(status);

      /* See which widget this event is for */
      if(widget == list->clist)
      {
          GtkCList *clist = GTK_CLIST(widget);

          /* Check if the widget that the event occured over was
           * the clist
           */
          GtkWidget *w = gtk_get_event_widget((GdkEvent *)motion);
          if(widget == w)
          {
            /* Send a "button_press_event" signal to the
             * GtkCList if the event was not sent yet and
             * button 1 is held
             *
             * This is so that the GtkCList catches the pointer
             * when it enters it
             */
            if(!list->initial_list_button_press_sent)
            {
                gint x, y;
                GdkModifierType mask;
                GdkWindow *window = clist->clist_window;
                GdkEvent ev;
                GdkEventButton *button = (GdkEventButton *)&ev;

                gdk_window_get_pointer(window, &x, &y, &mask);

                button->type = GDK_BUTTON_PRESS;
                button->window = window;
                button->send_event = TRUE;
                button->time = motion->time;
                button->x = motion->x;
                button->y = motion->y;
                button->pressure = 1.0;
                button->xtilt = 0.0;
                button->ytilt = 0.0;
                button->button = 1;
                button->state = mask;
                button->source = 0;
                button->deviceid = 0;
                button->x_root = 0;
                button->y_root = 0;

                if(mask & GDK_BUTTON1_MASK)
                  gtk_widget_event(w, &ev);

                status = TRUE;
            }
          }
          else
          {
            /* Moved over one of the other GtkWidgets belonging
             * to the Popup List?
             */
            if((w == list->vscrollbar) ||
               (w == list->hscrollbar) ||
               (w == list->scrolled_window) ||
               (w == list->toplevel)
            )
            {
                /* Forward event to that GtkWidget */
                gtk_widget_event(w, (GdkEvent *)motion);
            }
          }
      }

      return(status);
}


/*
 *    Popup List's Shadow paint signal callback.
 */
static gint PUListShadowPaintCB(pulist_struct *list)
{
      PUListShadowDraw(list);
      return(FALSE);
}

/*
 *    Draws the Popup List's Shadow.
 */
static void PUListShadowDraw(pulist_struct *list)
{
      gint width, height;
      GdkPixmap *pixmap;
      GdkWindow *window;
      GtkStyle *style;
      GtkWidget *w = (list != NULL) ? list->shadow : NULL;
      if(w == NULL)
          return;

      window = w->window;
      pixmap = list->shadow_pm;
      if((window == NULL) || (pixmap == NULL))
          return;

      gdk_window_get_size(pixmap, &width, &height);
      style = gtk_widget_get_style(w);

      gdk_draw_pixmap(
          window, style->white_gc, pixmap,
          0, 0, 0, 0, width, height
      );                           
}

/*
 *    Configures the Popup List's Shadow to the new geometry.
 *
 *    The shadow cast offset will be applied to the specified
 *    position.
 */
static void PUListShadowConfigure(                
      pulist_struct *list, gint x, gint y, gint width, gint height
)
{
      GtkWidget *w = (list != NULL) ? list->shadow : NULL;
      if(w == NULL)
          return;

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

      /* Update shadow geometry */
      gtk_widget_set_uposition(
          w,
          x + POPUP_LIST_SHADOW_OFFSET_X,
          y + POPUP_LIST_SHADOW_OFFSET_Y
      );
      gtk_widget_set_usize(
          w, width, height
      );
      gtk_widget_queue_resize(w);
}


/*
 *    Sets up the Popup List's GtkCList for the start of the drag.
 */
static void PUListCListDoDragSetUp(pulist_struct *list)
{
      GtkWidget *w = (list != NULL) ? list->clist : NULL;
      if(w == NULL)
          return;

      gtk_widget_grab_focus(w);
      gtk_widget_grab_default(w);
      if(w != gtk_grab_get_current())
          gtk_grab_add(w);
}

/*
 *    Removes all grabs from the Popup List's GtkCList, marking the
 *    end of the drag.
 */
static void PUListCListDoDragCleanUp(pulist_struct *list)
{
      GtkWidget *w = (list != NULL) ? list->clist : NULL;
      if(w == NULL)
          return;

      gtk_grab_remove(w);
}


/*
 *    Map Button GtkDrawingArea "expose" event signal callback.
 */
static gint PUListMapButtonExposeCB(
      GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
      gint status = FALSE;
      gint y, y_inc, width, height;
      GtkStateType state;
      GdkWindow *window;
      GdkDrawable *drawable;
      GdkGC *gc;
      GtkWidget *button;
      GtkStyle *style;
      if(widget == NULL)
          return(status);

      /* Get parent of given GtkDrawingArea widget, which should
       * be a GtkButton
       */
      button = widget->parent;
      if(button == NULL)
          return(status);

      state = GTK_WIDGET_STATE(widget);
      window = widget->window;
      style = gtk_widget_get_style(button);
      if((window == NULL) || (style == NULL))
          return(status);

      drawable = window;

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

      /* Begin drawing */

      /* Background */
      gtk_style_apply_default_background(
          style, drawable, FALSE, state,
          NULL,
          0, 0, width, height
      );

      /* Details */
      y_inc = 5;
      for(y = (gint)(height * 0.5f) - 1;
          y >= 0;
          y -= y_inc
      )
      {
          gc = style->light_gc[state];
          gdk_draw_line(
            drawable, gc,
            0, y + 0, width, y + 0
          );
          gc = style->dark_gc[state];
          gdk_draw_line(
            drawable, gc,
            0, y + 1, width, y + 1
          );
#if 0
          gc = style->black_gc;
          gdk_draw_line(
            drawable, gc,
            0, y + 1, width, y + 1
          );
#endif
      }
      for(y = (gint)(height * 0.5) - 1 + y_inc;
          y < height;
          y += y_inc
      )
      {
          gc = style->light_gc[state];
          gdk_draw_line(
            drawable, gc,
            0, y + 0, width, y + 0
          );
          gc = style->dark_gc[state];
          gdk_draw_line(
            drawable, gc,
            0, y + 1, width, y + 1
          );
#if 0
          gc = style->black_gc;
          gdk_draw_line(
            drawable, gc,
            0, y + 1, width, y + 1
          );
#endif
      }

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

      status = TRUE;

      return(status);
}


/*
 *    Popup List Box GtkDrawingArea event signal callback.
 */
static gint PUListBoxEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      gint status = FALSE;
      gboolean key_press;
      GdkEventFocus *focus;
      GdkEventKey *key;
      GdkEventButton *button;
      pulistbox_struct *box = PULISTBOX(data);
      if((widget == NULL) || (event == NULL) || (box == NULL))
          return(status);

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

        case GDK_FOCUS_CHANGE:
          focus = (GdkEventFocus *)event;
          if(focus->in && !GTK_WIDGET_HAS_FOCUS(widget))
          {
            GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
            gtk_widget_queue_draw(widget);
            status = TRUE;
          }
          else if(!focus->in && GTK_WIDGET_HAS_FOCUS(widget))
          {
            GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
            gtk_widget_queue_draw(widget);
            status = TRUE;
          }
          break;

        case GDK_KEY_PRESS:
        case GDK_KEY_RELEASE:
          key = (GdkEventKey *)event;
          key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;
#define STOP_KEY_SIGNAL_EMIT    {               \
if(widget != NULL)                              \
 gtk_signal_emit_stop_by_name(                  \
  GTK_OBJECT(widget),                           \
  key_press ?                                   \
   "key_press_event" : "key_release_event"      \
 );                                             \
}
          switch(key->keyval)
          {
            case GDK_Return:
            case GDK_KP_Enter:
            case GDK_space:
            if(key_press)
                PUListBoxMapCB(box->map_btn, box);
            STOP_KEY_SIGNAL_EMIT
            status = TRUE; 
            break;

            case GDK_Up:
            case GDK_KP_Up:
            if(key_press)
            {
                pulist_struct *list = PUListBoxGetPUList(box);
                GtkCList *clist = (GtkCList *)PUListGetCList(list);
                if(clist != NULL)
                {
                  GList *glist = clist->selection_end;
                  gint row = (glist != NULL) ? (gint)glist->data : -1;
                  if(row < 0)
                      row = 0;
                  else 
                      row--;
                  if((row >= 0) && (row < clist->rows))
                  {
                      gtk_clist_unselect_all(clist);
                      gtk_clist_select_row(clist, row, 0);

                      gtk_widget_queue_draw(widget);

                      /* Report new value */
                      if(box->changed_cb != NULL)
                        box->changed_cb(
                            box,          /* Popup List Box */
                            row,          /* Item */
                            box->changed_data   /* Data */
                        );
                  }
                }
            }
            STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;

            case GDK_Down:
            case GDK_KP_Down:
            if(key_press)
            {
                pulist_struct *list = PUListBoxGetPUList(box);
                GtkCList *clist = (GtkCList *)PUListGetCList(list);
                if(clist != NULL)
                {
                  GList *glist = clist->selection_end;
                  gint row = (glist != NULL) ? (gint)glist->data : -1;
                  if(row < 0)
                      row = 0;
                  else
                      row++;
                  if((row >= 0) && (row < clist->rows))
                  {
                      gtk_clist_unselect_all(clist);
                      gtk_clist_select_row(clist, row, 0);

                      gtk_widget_queue_draw(widget);

                      /* Report new value */
                      if(box->changed_cb != NULL)
                        box->changed_cb(
                            box,          /* Popup List Box */
                            row,          /* Item */
                            box->changed_data   /* Data */
                        );
                  }
                }
            }
            STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;
          }
          break;

        case GDK_BUTTON_PRESS:
          button = (GdkEventButton *)event;
          if(!GTK_WIDGET_HAS_FOCUS(widget))
            gtk_widget_grab_focus(widget);
          if(button->button == 1)
            PUListBoxMapCB(box->map_btn, box);
          status = TRUE;
          break;
      }

      return(status);
}

/*
 *    Popup List Box map callback.
 */
static void PUListBoxMapCB(GtkWidget *widget, gpointer data)
{
      const gchar *v;
      pulist_struct *list;
      pulistbox_struct *box = PULISTBOX(data);
      if((widget == NULL) || (box == NULL))
          return;

      list = PUListBoxGetPUList(box);
      if(list == NULL)
          return;

      /* Map Popup List and wait for user response */
      v = PUListMapQuery(
          list,
          NULL,         /* Initial value */
          -1,                 /* Lines visible */
          PULIST_RELATIVE_BELOW,
          box->toplevel,
          widget
      );

      /* Got new value? */
      if(v != NULL)
      {
          gint i = PUListFindItemFromValue(list, v);

          gtk_widget_queue_draw(box->da);

          /* Report new value */
          if(box->changed_cb != NULL)
            box->changed_cb(
                box,          /* Popup List Box */
                i,                  /* Item */
                box->changed_data   /* Data */
            );
      }
}

/*
 *    Redraws the Popup List Box.
 */
static void PUListBoxDraw(pulistbox_struct *box)
{
      const gint  frame_border = 2;
      gint width, height, font_height;
      GdkFont *font;
      GdkDrawable *drawable;
      GdkWindow *window;
      GtkStateType state;
      GtkStyle *style;
      pulist_struct *list;
      GtkWidget *w = (box != NULL) ? box->da : NULL;
      if(w == NULL)
          return;

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

      gdk_window_get_size(window, &width, &height);
      font = style->font;
      if((font == NULL) || (width <= 0) || (height <= 0))
          return;
               
      font_height = font->ascent + font->descent;

      drawable = (GdkDrawable *)window;

      /* Draw background */
      gdk_draw_rectangle(
          drawable, style->base_gc[state], TRUE,
          0, 0, width, height
      );

      /* Draw value */
      if(list->clist != NULL)
      {
          /* Get selected row or first row */
          GdkGC *gc = style->text_gc[state];
          GtkCList *clist = GTK_CLIST(list->clist);
          GList *glist = clist->selection_end;
          gint row = (glist != NULL) ? (gint)glist->data : 0;

          if((row >= 0) && (row < clist->rows))
          {
            gint x = frame_border, y;
            gchar *text = NULL;
            guint8 spacing = 0;
            GdkPixmap *pixmap = NULL;
            GdkBitmap *mask = NULL;

            /* Get icon and text */
            switch(gtk_clist_get_cell_type(clist, row, 0))
            {
              case GTK_CELL_TEXT:
                gtk_clist_get_text(clist, row, 0, &text);
                break;
              case GTK_CELL_PIXTEXT:
                gtk_clist_get_pixtext(
                  clist, row, 0, &text,
                  &spacing, &pixmap, &mask
                );
                break;
              case GTK_CELL_PIXMAP:
                gtk_clist_get_pixmap(
                  clist, row, 0, &pixmap, &mask
                );
                break;
              case GTK_CELL_WIDGET:
              case GTK_CELL_EMPTY:
                break;
            }
            /* Draw icon? */
            if(pixmap != NULL)
            {
                gint pm_width, pm_height;
                gdk_window_get_size(pixmap, &pm_width, &pm_height);
                y = (height - pm_height) / 2;
                gdk_gc_set_clip_mask(gc, mask);
                gdk_gc_set_clip_origin(gc, x, y);
                gdk_draw_pixmap(
                  drawable, gc, pixmap,
                  0, 0, x, y, pm_width, pm_height
                );
                gdk_gc_set_clip_mask(gc, NULL);
                x += pm_width + (gint)spacing;
            }
            /* Draw text? */
            if(text != NULL)
            {
                GdkTextBounds b;
                gdk_string_bounds(font, text, &b);
                gdk_draw_string(
                  drawable, font, gc,
                  x - b.lbearing,
                  ((height - font_height) / 2) +
                      font->ascent,
                  text
                );
            }
          }
      }

      /* Draw frame */
      gtk_draw_shadow(
          style, drawable, state, GTK_SHADOW_IN,
          0, 0, width - 1, height - 1
      );

      /* Draw focus if widget is focused */
      if(GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w))
          gtk_draw_focus(
            style, drawable,
            0, 0, width - 1, height - 1
          );

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

/*
 *    Returns the Popup List item index that matches the specified
 *    text.
 */
gint PUListFindItemFromValue(pulist_struct *list, const gchar *value)
{
      gint i;
      gchar *text;
      guint8 spacing;
      GdkPixmap *pixmap;
      GdkBitmap *mask;
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if((clist == NULL) || (value == NULL))
          return(-1);

      for(i = 0; i < clist->rows; i++)
      {
          text = NULL;
          switch(gtk_clist_get_cell_type(clist, i, 0))
          {
            case GTK_CELL_TEXT:
            gtk_clist_get_text(
                clist, i, 0, &text
            );
            break;
            case GTK_CELL_PIXTEXT:
            gtk_clist_get_pixtext(
                clist, i, 0, &text,
                &spacing, &pixmap, &mask
            );
            break;
            case GTK_CELL_PIXMAP:
            case GTK_CELL_WIDGET:
            case GTK_CELL_EMPTY:
            break;
          }
          if(text != NULL)
          {
            if((text == value) || !strcmp(text, value))
                return(i);
          }
      }
       
      return(-1);
}

/*
 *    Returns the Popup List item's data that matches the
 *    specified text.
 */
gpointer PUListGetDataFromValue(
      pulist_struct *list, const gchar *value
)
{
      gint i;
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if((clist == NULL) || (value == NULL))
          return(NULL);

      i = PUListFindItemFromValue(list, value);
      return((i >= 0) ?
          gtk_clist_get_row_data(clist, i) : NULL
      );
}

/*
 *    Returns the Popup List's toplevel GtkWindow.
 */
GtkWidget *PUListGetToplevel(pulist_struct *list)
{
      return((list != NULL) ? list->toplevel : NULL);
}

/*
 *    Returns the Popup List's GtkCList.
 */
GtkWidget *PUListGetCList(pulist_struct *list)
{
      return((list != NULL) ? list->clist : NULL);
}


/*
 *    Adds a new item to Popup List with the specified text.
 */
gint PUListAddItem(
      pulist_struct *list, const gchar *value
)
{
      return(PUListAddItemPixText(list, value, NULL, NULL));
}

/*
 *    Adds a new item to Popup List with the specified icon & text.
 */
gint PUListAddItemPixText(
      pulist_struct *list, const gchar *value,
      GdkPixmap *pixmap, GdkBitmap *mask
)
{
      gint i;
      gchar **strv;
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if((clist != NULL) ? (clist->columns <= 0) : TRUE)
          return(-1);

      /* Allocate cell values for new row */
      strv = (gchar **)g_malloc(
          clist->columns * sizeof(gchar *)
      );
      if(strv == NULL)
          return(-1);
      for(i = 0; i < clist->columns; i++)
          strv[i] = "";

      /* Append a new row */
      i = gtk_clist_append(clist, strv);

      /* Delete cell values */
      g_free(strv);

      if(i < 0)
          return(-1);

      PUListSetItemPixText(list, i, value, pixmap, mask);

      return(i);
}

/*
 *    Deletes all items in the Popup List.
 */
void PUListClear(pulist_struct *list)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;

      gtk_clist_freeze(clist);
      gtk_clist_clear(clist);
      gtk_clist_thaw(clist);
}


/*
 *    Sets the Popup List item's text.
 */
void PUListSetItemText(
      pulist_struct *list, gint i,
      const gchar *value
)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;

      if((i < 0) || (i >= clist->rows))
          return;

      gtk_clist_set_text(
          clist, i, 0,
          (value != NULL) ? value : ""
      );
}

/*
 *    Sets the Popup List item's icon & text.
 */
void PUListSetItemPixText(
      pulist_struct *list, gint i,
      const gchar *value,
      GdkPixmap *pixmap, GdkBitmap *mask
)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;      

      if((i < 0) || (i >= clist->rows))
          return;

      if(pixmap != NULL)
          gtk_clist_set_pixtext(
            clist, i, 0,
            (value != NULL) ? value : "",
            2, pixmap, mask
          );
      else
          gtk_clist_set_text(
            clist, i, 0,
            (value != NULL) ? value : ""  
          );   
}

/*
 *    Sets the Popup List item's data.
 */
void PUListSetItemData(
      pulist_struct *list, gint i,
      gpointer data
)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;

      if((i < 0) || (i >= clist->rows))
          return;

      gtk_clist_set_row_data(clist, i, data);
}

/*
 *    Sets the Popup List item's data & destroy callback.
 */
void PUListSetItemDataFull(
      pulist_struct *list, gint i,
      gpointer data, GtkDestroyNotify destroy_cb
)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;

      if((i < 0) || (i >= clist->rows))
          return;

      if(destroy_cb != NULL)
          gtk_clist_set_row_data_full(clist, i, data, destroy_cb);
      else
          gtk_clist_set_row_data(clist, i, data);
}


/*
 *    Gets the Popup List item's text.
 */
void PUListGetItemText(
      pulist_struct *list, gint i,
      gchar **value
)
{
      PUListGetItemPixText(list, i, value, NULL, NULL);
}

/*
 *    Gets the Popup List item's icon & text.
 */
void PUListGetItemPixText(
      pulist_struct *list, gint i,
      gchar **value,
      GdkPixmap **pixmap, GdkBitmap **mask
)
{
      gchar *ltext = NULL;
      guint8 lspacing = 0;
      GdkPixmap *lpixmap = NULL;
      GdkBitmap *lmask = NULL;
      GtkCList *clist;

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

      clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;

      if((i < 0) || (i >= clist->rows))
          return;

      switch(gtk_clist_get_cell_type(clist, i, 0))
      {
        case GTK_CELL_TEXT:
          gtk_clist_get_text(
            clist, i, 0, &ltext
          );
          break;
        case GTK_CELL_PIXTEXT:
          gtk_clist_get_pixtext(
            clist, i, 0, &ltext,
            &lspacing, &lpixmap, &lmask
          );
          break;
        case GTK_CELL_PIXMAP:
          gtk_clist_get_pixmap(
            clist, i, 0, &lpixmap, &lmask
          );
          break;
        case GTK_CELL_WIDGET:
        case GTK_CELL_EMPTY:
          break;
      }

      if(value != NULL)
          *value = ltext;
      if(pixmap != NULL)
          *pixmap = lpixmap;
      if(mask != NULL)
          *mask = lmask;
}

/*
 *    Gets the Popup List item's data.
 */
gpointer PUListGetItemData(pulist_struct *list, gint i)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)                        
          return(NULL);

      if((i < 0) || (i >= clist->rows))
          return(NULL);

      return(gtk_clist_get_row_data(clist, i));
}


/*
 *    Returns the last selected item index on the Popup List.
 */
gint PUListGetSelectedLast(pulist_struct *list)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      GList *glist = (clist != NULL) ? clist->selection_end : NULL;
      return((glist != NULL) ? (gint)glist->data : -1);
}

/*
 *    Selects the Popup List item specified by row.
 */
void PUListSelect(pulist_struct *list, gint i)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;

      if((i < 0) || (i >= clist->rows))
          return;

      gtk_clist_freeze(clist);

      gtk_clist_unselect_all(clist);
      gtk_clist_select_row(clist, i, 0);

      if(gtk_clist_row_is_visible(clist, i) !=
          GTK_VISIBILITY_FULL
      )
          gtk_clist_moveto(
            clist,
            i, -1,      /* Row, column */
            0.5f, 0.0f  /* Row, column */
          );

      gtk_clist_thaw(clist);
}

/*
 *    Unselects all items in the Popup List.
 */
void PUListUnselectAll(pulist_struct *list)
{
      GtkCList *clist = (GtkCList *)PUListGetCList(list);
      if(clist == NULL)
          return;

      gtk_clist_freeze(clist);
      gtk_clist_unselect_all(clist);
      gtk_clist_thaw(clist);
}


/*
 *    Checks if the Popup List is querying (if it is mapped).
 */
gboolean PUListIsQuery(pulist_struct *list)
{
      GtkWidget *w = (list != NULL) ? list->toplevel : NULL;
      return((w != NULL) ? GTK_WIDGET_MAPPED(w) : FALSE);
}

/*
 *    Breaks the Popup List query and unmaps it.
 */
void PUListBreakQuery(pulist_struct *list)
{
      if(!PUListIsQuery(list))
          return;

      while(list->gtk_block_level > 0)
      {
          list->gtk_block_level--;
          gtk_main_quit();
      }
}

/*
 *    Maps the Popup List and blocks for user input.
 *
 *    The Popup List will be mapped relative to the rel_widget.
 *
 *    Returns the selected value, the returned pointer must not be
 *    modified or deleted.
 *
 *    If the user clicks outside of the Popup List or the escape
 *    key is pressed then NULL will be returned.
 */
const gchar *PUListMapQuery(
      pulist_struct *list,
      const gchar *value,           /* Initial value */
      gint lines_visible,           /* Can be -1 for default */
      pulist_relative relative,     /* One of PULIST_RELATIVE_* */
      GtkWidget *rel_widget,        /* Map relative to this widget */
      GtkWidget *map_widget         /* Widget that mapped this list */
)
{
      gint sel_item = -1;
      gint  x, y,
            width = POPUP_LIST_DEF_WIDTH,
            height = POPUP_LIST_DEF_HEIGHT;
      gint root_width, root_height;
      GdkWindow *root;
      GtkWidget *w, *toplevel, *shadow;


      if(list == NULL)
          return(NULL);

      /* Get toplevel and check if it is already mapped */
      toplevel = w = list->toplevel;
      if((w != NULL) ? GTK_WIDGET_MAPPED(w) : TRUE)
          return(NULL);

      /* Get root window */
      root = gdk_window_get_parent(toplevel->window);

      /* Get shadow */
      shadow = list->shadow;

      /* Reset values */
      g_free(list->last_value);
      list->last_value = NULL;
      list->initial_list_button_press_sent = FALSE;

      /* Get root window geometry */
      gdk_window_get_size(root, &root_width, &root_height);

      /* Calculate size of list based on the size of the relative
       * widget
       */
      w = rel_widget;
      if(w != NULL)
          width = w->allocation.width;
      height = ((lines_visible < 0) ? 10 : lines_visible) *
          POPUP_LIST_ROW_SPACING;

      /* If map widget is given, then we need to restore its state
       * in preparation to mapping of the popup list.
       */
      list->map_widget = w = map_widget;
      if(w != NULL)
      {
          /* Remove grab from map widget as needed */
          gtk_grab_remove(w);

          /* Handle additional restoring by widget type */
          if(GTK_IS_BUTTON(w))
          {
            GtkButton *button = GTK_BUTTON(w);
            button->in_button = 1;
            button->button_down = 1;
          }
      }

      /* If a value has been specified, then select the item that
       * matches the specified value
       */
      w = list->clist;
      if((w != NULL) && (value != NULL))
      {
          sel_item = PUListFindItemFromValue(list, value);
          if(sel_item > -1)
          {
            GtkCList *clist = GTK_CLIST(w);
            gtk_clist_select_row(clist, sel_item, 0);
          }
      }

      /* Move and resize the toplevel & shadow */
      if((toplevel != NULL) && (shadow != NULL) && (root != NULL))
      {
          /* Relative widget specified? */
          w = rel_widget;
          if((w != NULL) ? (w->window != NULL) : FALSE)
          {
            GdkWindow *window = w->window;
            GtkAllocation *alloc = &w->allocation;

            /* Get coordinates of the relative widget with
             * respect to the desktop
             */
            gdk_window_get_root_position(window, &x, &y);
            if(GTK_WIDGET_NO_WINDOW(w))
            {
                x += alloc->x;
                y += alloc->y;
            }
            /* Adjust coordinates to be appropriate to the 
             * specified map relativity
             */
            switch(relative)
            {
              case PULIST_RELATIVE_CENTER:
                y = y - (height / 2) + (alloc->height / 2);
                break;
              case PULIST_RELATIVE_UP:
                y = y - height + alloc->height;
                break;     
              case PULIST_RELATIVE_DOWN:
                break;
              case PULIST_RELATIVE_ABOVE:
                y = y - height;
                break;
              case PULIST_RELATIVE_BELOW:
                y = y + alloc->height;
                break;
              }
          }
          else
          {
            /* Relative widget not specified, so get the
             * pointer's coordinates
             */
            gint px = 0, py = 0;
            GdkModifierType mask;

            gdk_window_get_pointer(root, &px, &py, &mask);
            x = px - (width / 2);
            y = py - (height / 2);
          }
          /* Clip coordinates with respect to the desktop */
          if(x > (root_width - width))
            x = root_width - width;
          if(x < 0)
            x = 0;
          if(y > (root_height - height))
            y = root_height - height;
          if(y < 0)
            y = 0;

          /* Move toplevel & shadow */
          gtk_widget_set_uposition(toplevel, x, y);
          gdk_window_move(toplevel->window, x, y);
          gtk_widget_set_uposition(shadow, x, y);
          gdk_window_move(shadow->window, x, y);

          /* Resize toplevel & shadow */
          gtk_widget_set_usize(toplevel, width, height);
          gdk_window_resize(toplevel->window, width, height);
          gtk_widget_set_usize(shadow, width, height);
          gdk_window_resize(shadow->window, width, height);

          /* Notify toplevel to resize */
/*        gtk_widget_queue_resize(toplevel); */
      }

      /* Recreate shadow pixmap */
      if(root != NULL)
      {
          GdkPixmap *pixmap = GDK_PIXMAP_NEW(width, height);

          GDK_PIXMAP_UNREF(list->shadow_pm);
          list->shadow_pm = pixmap;

          if(pixmap != NULL)   
          {
            GdkWindow *window = shadow->window;
            GdkColor *c, shadow_color;
            GdkColormap *colormap = gdk_window_get_colormap(window);
            GdkGC *gc = GDK_GC_NEW();

            c = &shadow_color;
            c->red = 0x5fff;
            c->green = 0x5fff;
            c->blue = 0x5fff;
            GDK_COLORMAP_ALLOC_COLOR(colormap, c);

            gdk_gc_set_subwindow(gc, GDK_INCLUDE_INFERIORS);
            gdk_window_copy_area(
                pixmap, gc,
                0, 0,
                root,
                x + POPUP_LIST_SHADOW_OFFSET_X,
                y + POPUP_LIST_SHADOW_OFFSET_Y,
                width, height
            );
            gdk_gc_set_subwindow(gc, GDK_CLIP_BY_CHILDREN);

            gdk_gc_set_function(gc, GDK_AND);
            gdk_gc_set_foreground(gc, c);
            gdk_draw_rectangle(
                pixmap, gc, TRUE,
                0, 0, width, height
            );
            gdk_gc_set_function(gc, GDK_COPY);

            GDK_GC_UNREF(gc);
            GDK_COLORMAP_FREE_COLOR(colormap, c);
          }
      }


      /* Map toplevel */
      gtk_widget_show_raise(shadow);
      gtk_widget_show_raise(toplevel);

      /* Set up the clist in preperation for dragged selecting */
      PUListCListDoDragSetUp(list);

      /* Move list scroll to the selected item (if any) */
      if((list->clist != NULL) && (sel_item > -1))
          gtk_clist_moveto(
            GTK_CLIST(list->clist),
            sel_item, -1, 0.5f, 0.0f
          ); 


      /* Wait for user response by pushing a GTK block loop */
      if(list->gtk_block_level < 0)
          list->gtk_block_level = 0;
      list->gtk_block_level++;
      gtk_main();

      /* Broke out of GTK block loop */

      /* Remove grabs from clist and do clean up after dragged
       * selecting
       */
      PUListCListDoDragCleanUp(list);

      /* Unmap */
      gtk_widget_hide(toplevel);
      gtk_widget_hide(shadow);
      GDK_PIXMAP_UNREF(list->shadow_pm);
      list->shadow_pm = NULL;

#if 0
      /* Restore map widget */
      w = list->map_widget;
      if(w != NULL)
      {
          /* Handle additional state restoring by widget type */
          if(GTK_IS_BUTTON(w))
          {
/* TODO */

          }
      }
#endif

      /* Unset map widget */
      list->map_widget = NULL;

      return(list->last_value);
}


/*
 *    Creates a new Popup List.
 */
pulist_struct *PUListNew(void)
{
      GdkWindow *window;
      GtkWidget *w, *parent, *parent2;
      GtkCList *clist;
      pulist_struct *list = (pulist_struct *)g_malloc0(
          sizeof(pulist_struct)
      );
      if(list == NULL)
          return(list);

      /* Reset values */
      list->shadow_pm = NULL;
      list->map_widget = NULL;
      list->gtk_block_level = 0;
      list->last_value = NULL;
      list->initial_list_button_press_sent = FALSE;

      /* Create toplevel */
      list->toplevel = w = gtk_window_new(GTK_WINDOW_POPUP);
      gtk_widget_add_events(
          w,
          GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
      );
      gtk_widget_realize(w);
      window = w->window;
      if(window != NULL)
      {
          /* No decorations */
          gdk_window_set_decorations(window, 0);
          /* No functions */
          gdk_window_set_functions(window, 0);
      }
      gtk_widget_add_events(
          w,                                  
          GDK_STRUCTURE_MASK
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "delete_event",
          GTK_SIGNAL_FUNC(PUListDeleteEventCB), list
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "configure_event",
          GTK_SIGNAL_FUNC(PUListConfigureEventCB), list
      );
      parent = w;

      /* Main vbox */
      list->main_vbox = w = gtk_vbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(parent), w);
      gtk_widget_show(w);
      parent = w;

      w = gtk_frame_new(NULL);
      gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
      gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
      gtk_widget_show(w);
      parent2 = w;

      /* Scrolled window for clist */
      list->scrolled_window = w = gtk_scrolled_window_new(NULL, NULL);
      gtk_scrolled_window_set_policy(
          GTK_SCROLLED_WINDOW(w),
          GTK_POLICY_AUTOMATIC,
          GTK_POLICY_AUTOMATIC
      );
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_widget_show(w);
      list->vscrollbar = GTK_SCROLLED_WINDOW(w)->vscrollbar;
      list->hscrollbar = GTK_SCROLLED_WINDOW(w)->hscrollbar;
      parent2 = w;

      /* CList */
      list->clist = w = gtk_clist_new(1);
      clist = GTK_CLIST(w);
      GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT | GTK_CAN_FOCUS);
      gtk_widget_add_events(
          w,
          GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
          GDK_POINTER_MOTION_MASK
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "key_press_event",
          GTK_SIGNAL_FUNC(PUListKeyPressEventCB), list
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "key_release_event",
          GTK_SIGNAL_FUNC(PUListKeyPressEventCB), list
      );
      gtk_signal_connect_after(
          GTK_OBJECT(w), "button_press_event",
          GTK_SIGNAL_FUNC(PUListButtonPressEventCB), list
      );
      gtk_signal_connect_after(
          GTK_OBJECT(w), "button_release_event",
          GTK_SIGNAL_FUNC(PUListButtonPressEventCB), list
      );
      gtk_signal_connect_after(
          GTK_OBJECT(w), "motion_notify_event",
          GTK_SIGNAL_FUNC(PUListMotionNotifyEventCB), list
      );
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_widget_realize(w);
      gtk_clist_set_shadow_type(clist, GTK_SHADOW_IN);
      gtk_clist_set_selection_mode(clist, GTK_SELECTION_BROWSE);
      gtk_clist_set_column_width(clist, 0, 10);
      gtk_clist_set_row_height(clist, POPUP_LIST_ROW_SPACING);
      gtk_widget_show(w);


      /* Shadow */
      list->shadow = w = gtk_window_new(GTK_WINDOW_POPUP);
      gtk_widget_set_app_paintable(w, TRUE);
      gtk_signal_connect_object(
          GTK_OBJECT(w), "expose_event",
          GTK_SIGNAL_FUNC(PUListShadowPaintCB), (GtkObject *)list
      );
      gtk_signal_connect_object(
          GTK_OBJECT(w), "draw",
          GTK_SIGNAL_FUNC(PUListShadowPaintCB), (GtkObject *)list
      );
      gtk_window_set_policy(
          GTK_WINDOW(w), TRUE, TRUE, TRUE
      );
      gtk_widget_realize(w);
      window = w->window;
      if(window != NULL)
      {
          /* No decorations */
          gdk_window_set_decorations(window, 0);
          /* No functions */
          gdk_window_set_functions(window, 0);

          gdk_window_set_override_redirect(window, TRUE);
      }


      return(list);
}

/*
 *    Deletes the Popup List.
 */
void PUListDelete(pulist_struct *list)
{
      if(list == NULL)
          return;

      /* Break out of any remaining GTK main loop levels */
      while(list->gtk_block_level > 0)
      {
          list->gtk_block_level--;
          gtk_main_quit();
      }

      /* Begin destroying widgets */
      GTK_WIDGET_DESTROY(list->shadow);
      GTK_WIDGET_DESTROY(list->clist);
      GTK_WIDGET_DESTROY(list->scrolled_window);
      GTK_WIDGET_DESTROY(list->main_vbox);
      GTK_WIDGET_DESTROY(list->toplevel);

      GDK_PIXMAP_UNREF(list->shadow_pm);

      g_free(list->last_value);

      g_free(list);
}


/*
 *    Creates a new GtkButton that is to appear as a popup list
 *    map button.
 */
GtkWidget *PUListNewMapButton(
      void (*map_cb)(GtkWidget *, gpointer),
      gpointer client_data
)
{
      GtkWidget *w, *button;


      /* Create new button */
      button = w = gtk_button_new();
      if(button == NULL)
          return(button);

      /* Set standard fixed size for button */
      gtk_widget_set_usize(
          w,
          POPUP_LIST_MAP_BTN_WIDTH,
          POPUP_LIST_MAP_BTN_HEIGHT
      );
      /* Set map callback function as "pressed" signal as needed */
      if(map_cb != NULL)
          gtk_signal_connect_after(
            GTK_OBJECT(w), "pressed",
            GTK_SIGNAL_FUNC(map_cb), client_data
          );


      /* Create drawing area */
      w = gtk_drawing_area_new();
      gtk_widget_add_events(w, GDK_EXPOSURE_MASK);
      gtk_signal_connect(
          GTK_OBJECT(w), "expose_event",
          GTK_SIGNAL_FUNC(PUListMapButtonExposeCB), NULL
      );
      gtk_container_add(GTK_CONTAINER(button), w);
      gtk_widget_show(w);


      return(button);
}

/*
 *      Creates a new GtkButton that is to appear as a popup list
 *      map button with an arrow.
 */
GtkWidget *PUListNewMapButtonArrow(
      GtkArrowType arrow_type, GtkShadowType shadow_type,
      void (*map_cb)(GtkWidget *, gpointer),
      gpointer client_data
)
{
      GtkWidget *w, *button;


      /* Create new button */
      button = w = gtk_button_new();
      if(button == NULL)
          return(button);

      /* Set standard fixed size for button */
      gtk_widget_set_usize(
          w,
          POPUP_LIST_MAP_BTN_WIDTH,
          POPUP_LIST_MAP_BTN_HEIGHT
      );
      /* Set map callback function as "pressed" signal as needed */
      if(map_cb != NULL)
          gtk_signal_connect_after(
            GTK_OBJECT(w), "pressed",
            GTK_SIGNAL_FUNC(map_cb), client_data
          );

      /* Create arrow */
      w = gtk_arrow_new(arrow_type, shadow_type);
      gtk_container_add(GTK_CONTAINER(button), w);
      gtk_widget_show(w);

      return(button);
}


/*
 *    Returns the Popup List Box's toplevel widget.
 */
GtkWidget *PUListBoxGetToplevel(pulistbox_struct *box)
{
      return((box != NULL) ? box->toplevel : NULL);
}

/*
 *    Returns the Popup List Box's Popup List.
 */
pulist_struct *PUListBoxGetPUList(pulistbox_struct *box)
{
      return((box != NULL) ? box->pulist : NULL);
}


/*
 *    Creates a new Popup List Box.
 */
pulistbox_struct *PUListBoxNew(
      GtkWidget *parent,
      gint width, gint height
)
{
      GtkWidget *w;
      pulistbox_struct *box = PULISTBOX(g_malloc0(
          sizeof(pulistbox_struct)
      ));
      if(box == NULL)
          return(NULL);

      /* Toplevel hbox */
      box->toplevel = w = gtk_hbox_new(FALSE, 0);
      gtk_widget_set_usize(w, width, height);
      if(parent != NULL)
      {
          if(GTK_IS_BOX(parent))
            gtk_box_pack_start(
                GTK_BOX(parent), w,
                (width <= 0) ? TRUE : FALSE,
                (width <= 0) ? TRUE : FALSE,   
                0
            );   
          else
            gtk_container_add(
                GTK_CONTAINER(parent), w
            );
      }
      parent = w;

      /* GtkDrawingArea */
      box->da = w = gtk_drawing_area_new();
      GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
      gtk_widget_add_events(                 
          w,
          GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK |
          GDK_FOCUS_CHANGE_MASK |
          GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "expose_event",
          GTK_SIGNAL_FUNC(PUListBoxEventCB), box
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "focus_in_event",
          GTK_SIGNAL_FUNC(PUListBoxEventCB), box
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "focus_out_event",
          GTK_SIGNAL_FUNC(PUListBoxEventCB), box
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "key_press_event",
          GTK_SIGNAL_FUNC(PUListBoxEventCB), box
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "key_release_event",
          GTK_SIGNAL_FUNC(PUListBoxEventCB), box
      );   
      gtk_signal_connect(                                      
          GTK_OBJECT(w), "button_press_event",
          GTK_SIGNAL_FUNC(PUListBoxEventCB), box
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "button_release_event",             
          GTK_SIGNAL_FUNC(PUListBoxEventCB), box
      );
      if(height <= 0)
          gtk_widget_set_usize(w, -1, 20 + (2 * 2));
      gtk_box_pack_start(
          GTK_BOX(parent), w,
          (width <= 0) ? TRUE : FALSE,
          (width <= 0) ? TRUE : FALSE, 
          0
      );
      gtk_widget_show(w);

      /* Map button */
      box->map_btn = w = PUListNewMapButtonArrow(
          GTK_ARROW_DOWN, GTK_SHADOW_OUT,
          PUListBoxMapCB, box
      );
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      gtk_widget_show(w);


      /* Popup List */
      box->pulist = PUListNew();

      return(box);
}

/*
 *    Sets the Popup List Box's changed callback.
 */
void PUListBoxSetChangedCB(
      pulistbox_struct *box,
      void (*func)(
            pulistbox_struct *,     /* Popup List Box */
            gint,             /* Item */         
            gpointer          /* Data */ 
      ),   
      gpointer data 
)
{
      if(box == NULL)
          return;

      box->changed_cb = func;
      box->changed_data = data;
}

/*
 *    Maps the Popup List Box.
 */
void PUListBoxMap(pulistbox_struct *box)
{
      GtkWidget *w = (box != NULL) ? box->toplevel : NULL;
      if(w == NULL)
          return;

      gtk_widget_show(w);
}

/*
 *    Unmaps the Popup List Box.
 */
void PUListBoxUnmap(pulistbox_struct *box)
{
      GtkWidget *w = (box != NULL) ? box->toplevel : NULL;
      if(w == NULL)
          return;

      gtk_widget_hide(w);
}

/*
 *    Deletes the Popup List Box.
 */
void PUListBoxDelete(pulistbox_struct *box)
{
      if(box == NULL)
          return;

      PUListDelete(box->pulist);
      GTK_WIDGET_DESTROY(box->da);
      GTK_WIDGET_DESTROY(box->map_btn);
      GTK_WIDGET_DESTROY(box->toplevel);
      g_free(box);
}

Generated by  Doxygen 1.6.0   Back to index