Logo Search packages:      
Sourcecode: vertex version File versions

pulist.c

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

#include <gtk/gtk.h>
#ifdef __MSW__
# include <gdk/gdkwin32.h>
#else
# include <gdk/gdkx.h>
#endif
#include <gdk/gdkkeysyms.h>

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


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 void PUListCListDoDragSetUp(pulist_struct *list);
static void PUListCListDoDragCleanUp(pulist_struct *list);

static gint PUListMapButtonExposeCB(
      GtkWidget *widget, GdkEventExpose *expose, gpointer data
);

gpointer PUListGetDataFromValue(
      pulist_struct *list, const gchar *value
);

void PUListAddItem(
      pulist_struct *list, const gchar *value,
        gpointer client_data, GtkDestroyNotify destroy_cb
);
void PUListAddItemPixText(
        pulist_struct *list, const gchar *value,
        GdkPixmap *pixmap, GdkBitmap *mask,
        gpointer client_data, GtkDestroyNotify destroy_cb
);
void PUListClear(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. */
        gint popup_relativity,  /* 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);

GtkWidget *PUListNewMapButton(
      void (*map_cb)(GtkWidget *, gpointer),
      gpointer client_data
);
GtkWidget *PUListNewMapButtonArrow(
        gint arrow_type, gint shadow_type,
        void (*map_cb)(GtkWidget *, gpointer),
        gpointer client_data
);


#define POPUP_LIST_ROW_SPACING      20

#define POPUP_LIST_MAP_BTN_WIDTH    17
#define POPUP_LIST_MAP_BTN_HEIGHT   17

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


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

      if(!GTK_IS_CLIST(widget))
          return(status);
      else
          clist = GTK_CLIST(widget);

        /* Get event type. */
        etype = key->type;

        /* Get other key event values. */
        press = (etype == GDK_KEY_PRESS) ? TRUE : FALSE;
        keyval = key->keyval;
        state = key->state;

/* Calls gtk_main_quit() to break out of any GTK+ main loops pushed
 * by this popup list.
 */
#define DO_BREAK_GTK_MAIN_LOOP  \
{ \
 /* Break out of any block loops. */ \
 while(list->gtk_block_level > 0) \
 { \
  list->gtk_block_level--; \
  gtk_main_quit(); \
 } \
}

/* Macro to clamp the GtkAdjustment adj and emit a "value_changed"
 * signal.
 */
#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" \
 ); \
}

/* Macro to emit a signal stop for a key press or release depending
 * on the current event's type.
 */
#define DO_STOP_KEY_SIGNAL_EMIT \
{ \
 gtk_signal_emit_stop_by_name( \
  GTK_OBJECT(widget), \
  press ? "key_press_event" : "key_release_event" \
 ); \
}

      /* Note, only "key_press_events" seem to be reported and not
       * "key_release_events". So always check if press is TRUE.
       */

      if(1)
      {
          /* Handle by key value. */
          switch(keyval)
          {
              case GDK_space:
              case GDK_Return:
              case GDK_KP_Enter:
              case GDK_ISO_Enter:
              case GDK_3270_Enter:
                /* Handle only if control key modifier is not held, so
                 * that accelerator keys will get handled properly.
                 */
                if(press)
                {
                    /* Pointer button was released inside the clist,
                     * meaning we now have a matched item.
                     */
                    gint row;


                    /* Get last selected row or -1 for none. */
                    row = (clist->selection_end != NULL) ?
                        (gint)clist->selection_end->data : -1;

                    if((row >= 0) && (row < clist->rows))
                    {
                        gchar *strptr = NULL;
                        guint8 spacing;
                        GdkPixmap *pixmap;
                        GdkBitmap *mask;

                        switch((gint)gtk_clist_get_cell_type(
                            clist, row, 0
                        ))
                        {
                          case GTK_CELL_TEXT:
                            gtk_clist_get_text(clist, row, 0, &strptr);
                            break;

                          case GTK_CELL_PIXTEXT:
                            gtk_clist_get_pixtext(
                                clist, row, 0, &strptr,
                                &spacing, &pixmap, &mask
                            );
                            break;
                        }
                        /* Got value? If so then update the last_value
                         * on the popup list with this new value.
                         */
                        if(strptr != NULL)
                        {
                            g_free(list->last_value);
                            list->last_value = g_strdup(strptr);
                        }
                    }
                    DO_BREAK_GTK_MAIN_LOOP
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
                break;

              case GDK_Escape:
            if(press)
            {
                    DO_BREAK_GTK_MAIN_LOOP
            }
            DO_STOP_KEY_SIGNAL_EMIT
            status = TRUE;
                break;

              case GDK_Page_Up:
              case GDK_KP_Page_Up:
                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_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;

          }
      }

#undef DO_BREAK_GTK_MAIN_LOOP
#undef DO_ADJ_CLAMP_EMIT
#undef DO_STOP_KEY_SIGNAL_EMIT

      return(status);
}

/*
 *    GtkCList "button_press_event" and "button_release_event" signal
 *    callback.
 */
static gint PUListButtonPressEventCB(
        GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
      gint status = FALSE;
      gint etype, x, y;
      GtkWidget *child;
      pulist_struct *list = (pulist_struct *)data;
      if((widget == NULL) || (button == NULL) || (list == NULL))
          return(status);

      /* Get child widget the event occured over. */
      child = gtk_get_event_widget((GdkEvent *)button);

      /* Get event type. */
      etype = button->type;

/* Calls gtk_main_quit() to break out of any GTK+ main loops pushed
 * by this popup list.
 */
#define DO_BREAK_GTK_MAIN_LOOP      \
{ \
 /* Break out of any block loops. */ \
 while(list->gtk_block_level > 0) \
 { \
  list->gtk_block_level--; \
  gtk_main_quit(); \
 } \
}

/* Restores the map_widget set on the given list (if any). */
#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"); \
  } \
 } \
}

      /* Handle by event type. */
      switch(etype)
      {
          case GDK_BUTTON_PRESS:
          x = (gint)button->x;
          y = (gint)button->y;
          /* Button pressed outside of the clist? */
          if(child != widget)
          {
            /* Clicked on one of the scroll bars? */
            if((child == list->vscrollbar) ||
                   (child == list->hscrollbar) ||
               (child == list->scrolled_window) ||
               (child == list->toplevel)
            )
            {
                gtk_widget_event(child, (GdkEvent *)button);
            }
            else
            {
                /* All else assumed clicked in some other widget,
                 * so 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 initial_list_button_press_sent
           * as TRUE to indicate an initial button press was sent (regardless
           * if it was sent synthetically or not.
           */
          if(button->button == 1)
          {
                if(!list->initial_list_button_press_sent)
                    list->initial_list_button_press_sent = TRUE;
          }
          break;

          case GDK_BUTTON_RELEASE:
            x = (gint)button->x;
            y = (gint)button->y;
          switch(button->button)
          {
            case 1:
            /* Button released within the clist? */
            if((child == widget) &&
                   (x >= 0) && (x < widget->allocation.width) &&
                   (y >= 0) && (y < widget->allocation.height)
            )
            {
                /* Pointer button was released inside the clist,
                 * meaning we now have a matched item.
                 */
                gint row;
                GtkCList *clist = GTK_CLIST(widget);


                /* Get last selected row or -1 for none. */
                row = (clist->selection_end != NULL) ?
                  (gint)clist->selection_end->data : -1;

                if((row >= 0) && (row < clist->rows))
                {
                  gchar *strptr = NULL;
                  guint8 spacing;
                  GdkPixmap *pixmap;
                  GdkBitmap *mask;

                  switch((gint)gtk_clist_get_cell_type(
                      clist, row, 0
                  ))
                  {
                    case GTK_CELL_TEXT:
                      gtk_clist_get_text(clist, row, 0, &strptr);
                      break;

                          case GTK_CELL_PIXTEXT:
                            gtk_clist_get_pixtext(
                        clist, row, 0, &strptr,
                        &spacing, &pixmap, &mask
                      );
                            break;
                  }
                  /* Got value? If so then update the last_value
                   * on the popup list with this new value.
                   */
                  if(strptr != NULL)
                  {
                      g_free(list->last_value);
                      list->last_value = g_strdup(strptr);
                  }
                }

                DO_BREAK_GTK_MAIN_LOOP
            }
            else
            {
                /* Event occured while pointer was outside the widget.
                 *
                 * 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;
          }
          /* Need to restore the map widget on a button release. */
          DO_RESTORE_MAP_WIDGET
          break;
      }

#undef DO_RESTORE_MAP_WIDGET
#undef DO_BREAK_GTK_MAIN_LOOP

      return(status);
}

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

      if(GTK_IS_CLIST(widget))
          clist = GTK_CLIST(widget);
      else
          return(status);

        /* Get child widget the event occured over. */
        child = gtk_get_event_widget((GdkEvent *)motion);

      /* Motion occured within the clist? */
      if(child == widget)
      {
          /* Need to send the initial button press signal to the clist
           * so that it catches the pointer when it enters the clist?
           */
          if(!list->initial_list_button_press_sent)
          {
            GdkWindow *window = clist->clist_window;
            GdkEvent tmp_event;
            gint x, y;
            GdkModifierType mask;


            /* Transfer the grab over to the list by synthesizing
             * a button press event
             */
            gdk_window_get_pointer(window, &x, &y, &mask);

            tmp_event.button.type = GDK_BUTTON_PRESS;
            tmp_event.button.window = window;
            tmp_event.button.send_event = TRUE;
            tmp_event.button.time = GDK_CURRENT_TIME;
            tmp_event.button.x = motion->x;
            tmp_event.button.y = motion->y;
            /* Skip XInput fields, hopefully clist dosen't care. */
            tmp_event.button.button = 1;
            tmp_event.button.state = mask;

            /* Sent button event if button 1 is currently held down. */
            if(mask & GDK_BUTTON1_MASK)
                gtk_widget_event(widget, &tmp_event);
          }
      }

        return(status);
}

/*
 *    Sets up the given popup list's clist for the start of the drag.
 */
static void PUListCListDoDragSetUp(pulist_struct *list)
{
      GtkWidget *w;
      GtkCList *clist;
      GdkWindow *window;


      if(list == NULL)
          return;

      w = list->clist;
      if(w == NULL)
          return;

      clist = GTK_CLIST(w);
      window = clist->clist_window;

      /* Grab focus and input for the clist. */
      w = list->toplevel;
      if(w != NULL)
          gtk_widget_grab_focus(w);
      w = list->clist;
      if((w != NULL) && (w != gtk_grab_get_current()))
          gtk_grab_add(w);
}

/*
 *    Removes all grabs from the given popup list's clist, marking the
 *    end of the drag.
 */
static void PUListCListDoDragCleanUp(pulist_struct *list)
{
        GtkWidget *w;


        if(list == NULL)
            return;

        /* Remove grab from clist. */
      w = list->clist;
      if(w != NULL)
          gtk_grab_remove(w);
}



/*
 *    Draws the map button for the given GtkDrawingArea widget.
 */
static gint PUListMapButtonExposeCB(
        GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
      gint state, y, y_inc, width, height;
      GdkWindow *window;
      GtkWidget *button;
      GdkGC *gc;
      GtkStyle *style;
      if(widget == NULL)
          return(FALSE);

      /* Parent of given GtkDrawingArea widget is a GtkButton. */
      button = widget->parent;
      if(button == NULL)
          return(TRUE);

      window = widget->window;
      style = gtk_widget_get_style(button);
      if(style == NULL)
          style = gtk_widget_get_default_style();

      if((window == NULL) || (style == NULL))
          return(TRUE);

      state = GTK_WIDGET_STATE(widget);
      gdk_window_get_size(window, &width, &height);


        /* Clear window. */
      gc = style->bg_gc[state];
      if(gc != NULL)
      {
          GdkPixmap *bg_pixmap = style->bg_pixmap[state];
          if(bg_pixmap != NULL)
          {
            gdk_gc_set_fill(gc, GDK_TILED);
            gdk_gc_set_tile(gc, bg_pixmap);
          }

          gdk_draw_rectangle(
            (GdkDrawable *)window, gc, TRUE,
            0, 0, width, height
          );
      }

      /* Begin drawing details. */
      y_inc = 5;
      for(y = (gint)(height * 0.5) - 1;
            y >= 0;
            y -= y_inc
      )
      {
          gc = style->light_gc[state];
          gdk_draw_line(
                (GdkDrawable *)window, gc,
            0, y + 0, width, y + 0
          );
            gc = style->dark_gc[state];
            gdk_draw_line(
                (GdkDrawable *)window, gc,
                0, y + 1, width, y + 1
            );
/*
            gc = style->black_gc;
            gdk_draw_line(
                (GdkDrawable *)window, gc,
                0, y + 1, width, y + 1
            );
 */
      }
        for(y = (gint)(height * 0.5) - 1 + y_inc;
            y < height;
            y += y_inc
        )
        {
            gc = style->light_gc[state];
            gdk_draw_line(
                (GdkDrawable *)window, gc,
                0, y + 0, width, y + 0
            );
            gc = style->dark_gc[state];
            gdk_draw_line(
                (GdkDrawable *)window, gc,
                0, y + 1, width, y + 1
            );
/*
            gc = style->black_gc;
            gdk_draw_line(
                (GdkDrawable *)window, gc,
                0, y + 1, width, y + 1
            );
 */
        }

      return(TRUE);
}


/*
 *    Returns the client data value from the row that matches the
 *    given value.
 */
gpointer PUListGetDataFromValue(
        pulist_struct *list, const gchar *value
)
{
      gint row;
      GtkCList *clist;
      gchar *cell_text;
      guint8 spacing;
      GdkPixmap *pixmap;
      GdkBitmap *mask;


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

        clist = (GtkCList *)list->clist;
        if(clist == NULL)
            return(NULL);

      /* Iterate through all rows, finding the row that matches the
       * given value.
       */
      for(row = 0; row < clist->rows; row++)
      {
          cell_text = NULL;
          switch((gint)gtk_clist_get_cell_type(
            clist, row, 0
          ))
          {
              case GTK_CELL_TEXT:
                gtk_clist_get_text(clist, row, 0, &cell_text);
                break;

              case GTK_CELL_PIXTEXT:
                gtk_clist_get_pixtext(
                    clist, row, 0, &cell_text,
                    &spacing, &pixmap, &mask
                );
                break;
            }
            /* Got value for current row? */
            if(cell_text != NULL)
            {
                if(!strcmp(cell_text, value))
                {
                    /* Got match, return client data for this row. */
                return(gtk_clist_get_row_data(clist, row));
            }
          }
      }

      return(NULL);
}


/*
 *    Appends a new item to the popup list.
 */
void PUListAddItem(
      pulist_struct *list, const gchar *value,
        gpointer client_data, GtkDestroyNotify destroy_cb
)
{
      gint i, new_row;
      gchar **strv;
        GtkCList *clist;


        if(list == NULL)
            return;

        clist = (GtkCList *)list->clist;
        if(clist == NULL)
            return;

      /* Allocate values for new row. */
      strv = (gchar **)g_malloc(
          clist->columns * sizeof(gchar *)
      );
      if(strv != NULL)
      {
          for(i = 0; i < clist->columns; i++)
            strv[i] = g_strdup("X");
      }

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

      /* Deallocate row values. */
      if(strv != NULL)
      {
            for(i = 0; i < clist->columns; i++)
                g_free(strv[i]);
          g_free(strv);
          strv = NULL;
        }

        /* If new row was created successfully, then set text and
         * client data with destroy callback.
         */
      if((new_row > -1) && (value != NULL))
      {
          gtk_clist_set_text(clist, new_row, 0, value);
          gtk_clist_set_row_data_full(
            clist, new_row, client_data, destroy_cb
          );
      }
}

/*
 *    Same as PUListAddItem() except that it adds a pixmap and mask.
 */
void PUListAddItemPixText(
      pulist_struct *list, const gchar *value,
      GdkPixmap *pixmap, GdkBitmap *mask,
        gpointer client_data, GtkDestroyNotify destroy_cb
)
{
        gint i, new_row;
        gchar **strv;
        GtkCList *clist;


      /* If no pixmap is given then revert to calling PUListAddItem()
       * instead.
       */
      if(pixmap == NULL)
      {
          PUListAddItem(list, value, client_data, destroy_cb);
          return;
      }

        if(list == NULL)
            return;

        clist = (GtkCList *)list->clist;
        if(clist == NULL)
            return;

        /* Allocate values for new row. */
        strv = (gchar **)g_malloc(
            clist->columns * sizeof(gchar *)
        );
        if(strv != NULL)
        {
            for(i = 0; i < clist->columns; i++)
                strv[i] = g_strdup("X");
        }

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

        /* Deallocate row values. */
        if(strv != NULL)
        {
            for(i = 0; i < clist->columns; i++)
                g_free(strv[i]);
            g_free(strv);
            strv = NULL;
        }

      /* If new row was created successfully, then set pixtext and
       * client data with destroy callback.
       */
        if((new_row > -1) && (value != NULL))
        {
            gtk_clist_set_pixtext(
            clist, new_row, 0, value, 2, pixmap, mask
          );
            gtk_clist_set_row_data_full(
                clist, new_row, client_data, destroy_cb
            );
        }
}

/*
 *    Clears all items in the popup list.
 */
void PUListClear(pulist_struct *list)
{
      GtkCList *clist;


      if(list == NULL)
          return;

      clist = (GtkCList *)list->clist;
      if(clist == NULL)
          return;

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


/*
 *    Returns TRUE if the popup list is currently mapped.
 */
gboolean PUListIsQuery(pulist_struct *list)
{
      return((list != NULL) ? list->map_state : FALSE);
}

/*
 *    Unmaps the popup list and breaks query.
 */
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 relative to the GtkWidget rel_widget and
 *    blocks input untill a response is received.
 *
 *    If the user clicks outside of the list then the return will
 *    be NULL.
 */
const gchar *PUListMapQuery(
        pulist_struct *list,
        const gchar *value,   /* Initial value. */
        gint lines_visible,   /* Can be -1 for default. */
        gint popup_relativity,  /* One of PULIST_RELATIVE_*. */
        GtkWidget *rel_widget,      /* Map relative to this widget. */
        GtkWidget *map_widget   /* Widget that mapped this list. */
)
{
      gint sel_row = -1;
      gint width = 320, height = 150;
      gint root_width, root_height;
      GtkWidget *w, *toplevel;


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

      /* List already mapped? */
      if(list->map_state)
          return(NULL);

      /* Get toplevel widget of popup list. */
      toplevel = list->toplevel;
      if(toplevel == NULL)
          return(NULL);


        /* Reset last value. */
        g_free(list->last_value);
        list->last_value = NULL;

      list->initial_list_button_press_sent = FALSE;


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

      /* Get intended size of toplevel if relative widget is given. */
      w = rel_widget;
      if((w != NULL) ? (w->window != NULL) : FALSE)
      {
          GdkWindow *window = w->window;

          gdk_window_get_size(window, &width, &height);
          height = ((lines_visible < 0) ? 10 : lines_visible) *
            POPUP_LIST_ROW_SPACING;
      }
      else
      {
          /* No relative widget available, use default size. */
            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 state restoring by widget type. */
          if(GTK_IS_BUTTON(w))
          {
                GtkButton *button = GTK_BUTTON(w);
            button->in_button = 1;
                button->button_down = 1;
          }
      }

      /* If value is given, then select row on clist that matches
       * the given value (if any).
       */
      w = list->clist;
      if((w != NULL) && (value != NULL))
      {
          gint row;
          GtkCList *clist = GTK_CLIST(w);


          for(row = 0; row < clist->rows; row++)
          {
            gchar *strptr = NULL;
            guint8 spacing;
            GdkPixmap *pixmap;
            GdkBitmap *mask;

            switch((gint)gtk_clist_get_cell_type(
                clist, row, 0
            ))
            {
              case GTK_CELL_TEXT:
                    gtk_clist_get_text(clist, row, 0, &strptr);
                    break;

                  case GTK_CELL_PIXTEXT:
                    gtk_clist_get_pixtext(
                        clist, row, 0, &strptr,
                        &spacing, &pixmap, &mask
                    );
                    break;
                }
            /* Got value for current row? */
            if(strptr != NULL)
            {
                if(!strcmp(strptr, value))
                {
                  /* Got match, select row and break. */
                  gtk_clist_select_row(clist, row, 0);
                  sel_row = row;
                  break;
                }
            }
          }
      }

      /* Move toplevel to relative widget? */
      w = rel_widget;
      if((w != NULL) ? (w->window != NULL) : FALSE)
      {
          gint x, y, wwidth, wheight;
            GdkWindow *window = w->window;

          GUIGetWindowRootPosition(window, &x, &y);
            gdk_window_get_size(window, &wwidth, &wheight);
          switch(popup_relativity)
          {
            case PULIST_RELATIVE_UP:
            y = y - height + wheight;
            break;

              case PULIST_RELATIVE_DOWN:
                break;

              case PULIST_RELATIVE_ABOVE:
                y = y - height;
                break;

              case PULIST_RELATIVE_BELOW:
            y = y + wheight;
                break;

            default:
            y = y - (height / 2) + (wheight / 2);
            break;
          }

          /* Clip x and y coordinates. */
          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;

          gtk_widget_set_uposition(toplevel, x, y);
      }
      else
      {
          /* Relative widget not given, so map at root coordinates of
           * current pointer position.
           */
          gint x, y, px = 0, py = 0;
          GdkModifierType mask;


          gdk_window_get_pointer(
            (GdkWindow *)GDK_ROOT_PARENT(),
            &px, &py, &mask
          );
          x = px - (width / 2);
          y = py - (height / 2);

            /* Clip x and y coordinates. */
            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;

            gtk_widget_set_uposition(toplevel, x, y);
      }

      /* Set new toplevel size. */
      gtk_widget_set_usize(toplevel, width, height);
      /* Notify toplevel to resize. */
      gtk_widget_queue_resize(toplevel);


        /* Map toplevel. */
        gtk_widget_show_raise(toplevel);
        list->map_state = TRUE;

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

      /* Move to selected row (if any). */
      if((list->clist != NULL) && (sel_row > -1))
          gtk_clist_moveto(
            GTK_CLIST(list->clist),
            sel_row, -1, 0.5, 0.0
          ); 


      /* Push GTK+ block loop. */
      if(list->gtk_block_level < 0)
          list->gtk_block_level = 0;
      list->gtk_block_level++;
      gtk_main();


      /* Done with GTK+ block loop at this point. */

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

        /* Unmap toplevel. */
        gtk_widget_hide(toplevel);
        list->map_state = FALSE;

        /* Unset modal. */
/*        gtk_window_set_modal(GTK_WINDOW(toplevel), FALSE); */


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


            }
        }

      /* 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->map_state = FALSE;
      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);
        }
      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_clist_set_column_width(clist, 0, 100);
        gtk_clist_set_row_height(clist, POPUP_LIST_ROW_SPACING);
        gtk_clist_set_shadow_type(clist, GTK_SHADOW_IN);
      gtk_clist_set_selection_mode(clist, GTK_SELECTION_BROWSE);
        gtk_container_add(GTK_CONTAINER(parent2), w);
        gtk_widget_realize(w);
        gtk_widget_show(w);


      return(list);
}

/*
 *    Deallocates all resources on the given popup list.
 */
void PUListDelete(pulist_struct *list)
{
        GtkWidget **w;


      if(list == NULL)
          return;

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

#define DO_DESTROY_WIDGET     \
{ if(*w != NULL) { gtk_widget_destroy(*w); *w = NULL; } }

          /* Begin destroying widgets. */
            w = &list->clist;
            DO_DESTROY_WIDGET

            w = &list->scrolled_window;
          list->vscrollbar = NULL;
          list->hscrollbar = NULL;
            DO_DESTROY_WIDGET

            w = &list->main_vbox;
            DO_DESTROY_WIDGET

            w = &list->toplevel;
            DO_DESTROY_WIDGET

#undef DO_DESTROY_WIDGET
      }

      /* Deallocate other memory. */
      g_free(list->last_value);
      list->last_value = NULL;

      /* Deallocate structure itself. */
      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(
        gint arrow_type, gint 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);
}


Generated by  Doxygen 1.6.0   Back to index