#include #include #include #include #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)) #if defined(WIN32) gdk_draw_rectangle( drawable, style->fg_gc[state], FALSE, 0, 0, width - 1, height - 1 ); #else gtk_draw_focus( style, drawable, 0, 0, width - 1, height - 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 ); } /* * 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, <ext ); break; case GTK_CELL_PIXTEXT: gtk_clist_get_pixtext( clist, i, 0, <ext, &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); #if !defined(WIN32) gdk_window_set_override_redirect(window, TRUE); #endif } 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); }