/* list.c */ /* * List Widget - a scrollable window of text strings in which one or more * can be selected by clicking on them with the mouse. */ #include #include #include #include "lui.h" #include "list.h" /* * Redraw a single entry in the list */ static void draw_entry( LUI_LIST *list, int entry ) { char *str; int len; GC back_gc, text_gc; int row; int x, y; row = entry - list->topstring; if (row>=0 && rowrows) { x = LUI_Border; y = LUI_Border + row * FONT_HEIGHT; if (entry < list->numstrings) { if (list->select_flags[entry]) { /* draw as highlighted */ back_gc = LUI_GC_black; text_gc = LUI_GC_white; } else { /* draw normal */ back_gc = LUI_GC_gray; text_gc = LUI_GC_black; } XFillRectangle( LUI_Display, list->window, back_gc, x, y, list->width-2*LUI_Border, FONT_HEIGHT ); /* Don't worry about clipping text, X will take care of that */ str = list->strings[ entry ]; len = strlen( str ); XDrawString( LUI_Display, list->window, text_gc, x-list->leftoffset, y+LUI_Font_yoff, str, len ); } else { /* just redraw background */ XFillRectangle( LUI_Display, list->window, LUI_GC_gray, x, y, list->width-2*LUI_Border, FONT_HEIGHT ); } } } /* * Redraw whole list. */ static void draw_list( LUI_LIST *list ) { int i; for (i=0;irows;i++) { draw_entry( list, list->topstring+i ); } /* draw the frame */ LUI_DrawFrame( list->window, 0, 0, list->width, list->height, LUI_Border,1); } /* * Load an array of strings into the list widget. Then, redraw the widget. * Input: list - the list widget; * num - number of strings * strings - array of pointers to character arrays (like main's argv) * free_flag - 1=free strings when finished, 0=never free strings */ void LUI_ListLoad( LUI_LIST *list, int num, char **strings, int free_flag ) { float size; int i; if (!list) { printf("Error in LUI_ListLoad: NULL list argument\n"); return; } /* deallocate old list */ if (list->free_flag && list->strings) { for (i=0;inumstrings;i++) { free( list->strings[i] ); } free( list->strings ); } if (list->select_flags) { free( list->select_flags ); list->select_flags = NULL; } /* store new strings */ list->free_flag = free_flag; list->strings = strings; list->numstrings = num; if (num>0) { list->select_flags = (int *) calloc( num, sizeof(int) ); } list->topstring = 0; /* find width, in pixels, of longest line */ list->maxwidth = 0; for (i=0;i list->maxwidth) { list->maxwidth = overall.width; } } draw_list( list ); /* vertical scrollbar */ if (num==0) { size = 100.0; } else { size = 100.0 * (float) list->rows / (float) num; if (size>100.0) { size = 100.0; } } LUI_ScrollBarSet( list->vsb, size, 0.0 ); /* horizontal scrollbar */ if (list->hsb) { if (list->maxwidth==0) { size = 100.0; } else { size = 100.0 * (float) list->columns / (float) list->maxwidth; if (size>100.0) { size = 100.0; } } LUI_ScrollBarSet( list->hsb, size, 0.0 ); } } /* * Deallocate the strings in a list widget. The strings *must* have been * originally been allocated with malloc()! */ void LUI_ListUnload( LUI_LIST *list ) { if (list->free_flag && list->strings) { int i; for (i=0;inumstrings;i++) { free( list->strings[i] ); } free( list->strings ); free( list->select_flags ); list->strings = NULL; list->select_flags = NULL; list->numstrings = 0; draw_list( list ); LUI_ScrollBarSet( list->vsb, 100.0, 0.0 ); } } /* * Return the selection status of an entry in the list. * Input: list - which list widget * entry - which entry, 0 being the first * Return: 1 = selected, 0 = unselected. */ int LUI_ListGetStatus( LUI_LIST *list, int entry ) { if (entry>=0 && entrynumstrings) { return list->select_flags[entry]; } else { return 0; } } /* * Set the selection status of an entry in the list. * Input: list - which list widget * entry - which entry, 0 being the first * status - 1 = selected, 0 = unselected */ void LUI_ListSetStatus( LUI_LIST *list, int entry, int status ) { if (entry>=0 && entrynumstrings) { list->select_flags[entry] = status; if (entry>=list->topstring && entrytopstring+list->rows) { draw_entry( list, entry ); } } } /* * Set the selection status of all entries in the list. * Input: list - which list widget * status - 1 = selected, 0 = unselected */ void LUI_ListSetStatusAll( LUI_LIST *list, int status ) { int i; for (i=0;inumstrings;i++) { list->select_flags[i] = status; if (i>=list->topstring && itopstring+list->rows) { draw_entry( list, i ); } } } /* * Register a callback function to call when the user (un)selects an * entry in the list. * Input: list - which list * callback - the callback function. It should be declared as: * int callback( LUI_LIST *list, int entry, int state ) */ void LUI_ListCallback( LUI_LIST *list, int (*callback)() ) { list->callback = callback; list->context_index = context_index; } /* * When an X event occurs in the list window, this function will be called. * Input: list - which list widget * event - the X event */ static int list_process( LUI_LIST *list, XEvent *event ) { static int prev_entry = -1; switch (event->type) { case Expose: draw_list( list ); break; case ButtonPress: { /* main window */ int row, i; row = (event->xbutton.y - LUI_Border) / FONT_HEIGHT; i = list->topstring + row; if (inumstrings) { /* toggle status */ list->select_flags[i] = !list->select_flags[i]; prev_entry = i; draw_entry( list, i ); XSync( LUI_Display, 0 ); if (list->callback) { (*list->callback)( list, i, list->select_flags[i] ); } } } break; case MotionNotify: { /* main window */ int row, i; row = (event->xmotion.y - LUI_Border) / FONT_HEIGHT; if (row>=0 && rowrows) { i = list->topstring + row; #ifdef JUNK if (row>=list->rows && inumstrings) { /* scroll down */ list->topstring++; draw_list(list); LUI_ScrollBarSetPos( list->vsb, 100.0 * list->topstring / (list->numstrings-list->rows) ); } else if (row<0 && i>0) { /* scroll up */ list->topstring--; draw_list(list); LUI_ScrollBarSetPos( list->vsb, 100.0 * list->topstring / (list->numstrings-list->rows) ); } #endif if (i>=0 && inumstrings && i!=prev_entry) { int j; if (i>prev_entry) { for (j=prev_entry+1; j<=i; j++) { /* toggle status */ list->select_flags[j] = !list->select_flags[j]; draw_entry( list, j ); if (list->callback) { (*list->callback)( list, j, list->select_flags[j] ); } } } else { for (j=prev_entry-1;j>=i;j--) { /* toggle status */ list->select_flags[j] = !list->select_flags[j]; draw_entry( list, j ); if (list->callback) { (*list->callback)( list, j, list->select_flags[j] ); } } } prev_entry = i; } } } break; case ButtonRelease: prev_entry = -1; break; case EnterNotify: break; case LeaveNotify: break; default: printf("Error in list_process: unexpected event (%d)\n", (int) event->type ); } return 1; } /* * Callback for the vertical slider, when it moves this function is called. */ static int vscroll_cb( LUI_SCROLLBAR *sb, float pos ) { int newtop; LUI_LIST *list; list = (LUI_LIST *) sb->userdata; newtop = (list->numstrings - list->rows) * pos / 100.0; if (newtop != list->topstring) { list->topstring = newtop; draw_list( list ); } return 1; } /* * Callback for the horizontal slider, when it moves this function is called. */ static int hscroll_cb( LUI_SCROLLBAR *sb, float pos ) { int newleft; LUI_LIST *list; list = (LUI_LIST *) sb->userdata; newleft = (list->maxwidth - list->columns) * pos / 100.0; if (newleft != list->leftoffset) { list->leftoffset = newleft; draw_list( list ); } return 1; } /* * Create a new list widget. * Input: parent - parent window ID * x, y, location of list widget with respect to parent in pixels * width, height - outside dimensions of list widget in pixels * including the scroll bar. * hscroll - include a horizontal scrollbar? * Return: LUI_LIST pointer or NULL if error. */ LUI_LIST *LUI_ListCreate( Window parent, int x, int y, int width, int height, int hscroll ) { LUI_LIST *list; LUI_LayoutCheck( &x, &y, &width, &height ); list = (LUI_LIST *) malloc( sizeof(LUI_LIST) ); if (!list) { return NULL; } width -= (SLIDER_WIDTH + 2 * LUI_Border); if (hscroll) { height -= (SLIDER_WIDTH + 2 * LUI_Border); } list->x = x; list->y = y; list->width = width; list->height = height - 2; list->window = XCreateSimpleWindow( LUI_Display, parent, x, y, width, height-2, 1, LUI_Color_black, LUI_Color_gray ); LUI_EventAdd2( list->window, ExposureMask | ButtonPressMask | ButtonMotionMask | ButtonReleaseMask, (LUI_FNCP) list_process, list ); XMapWindow( LUI_Display, list->window ); list->vsb = LUI_ScrollBarCreate( parent, x + width + 2*LUI_Border, y, SLIDER_WIDTH, height-2, 1 ); LUI_ScrollBarData( list->vsb, list ); LUI_ScrollBarCallback( list->vsb, vscroll_cb ); if (hscroll) { list->hsb = LUI_ScrollBarCreate( parent, x, y + height + 2*LUI_Border, width, 20, 0 ); LUI_ScrollBarData( list->hsb, list ); LUI_ScrollBarCallback( list->hsb, hscroll_cb ); } else { list->hsb = NULL; } list->leftoffset = 0; list->strings = NULL; list->select_flags = NULL; list->numstrings = 0; list->rows = (list->height-2*LUI_Border) / FONT_HEIGHT; list->columns = list->width-2*LUI_Border; list->topstring = 0; return list; } /* * Destroy the given list widget. */ void LUI_ListDestroy( LUI_LIST *list ) { if (!list) { printf("Error in LUI_ListDestroy: NULL list argument\n"); return; } if (list->select_flags) { free( list->select_flags ); } if (list->window) { LUI_EventRemove( list->window ); XDestroyWindow( LUI_Display, list->window ); } free( list ); }