.\" This file is in the public domain. .Dd April 12, 2024 .Dt LIBSCROLLLIST 3 .Os NetBSD 1.4T .Sh NAME .Nm list_new , .Nm list_set_cookie , .Nm list_set_filter , .Nm list_change_filter , .Nm list_set_compare , .Nm list_change_compare , .Nm list_set_linecount , .Nm list_change_linecount , .Nm list_set_render , .Nm list_set_empty_item , .Nm list_clear , .Nm list_clear_cb , .Nm list_destroy , .Nm list_add1 , .Nm list_addn , .Nm list_set_lines , .Nm list_lines_wanted , .Nm list_cur_top , .Nm list_cur_hl_line , .Nm list_item_hl_ptr , .Nm list_item_hl_inx , .Nm list_set_hl_line , .Nm list_set_hl_inx , .Nm list_set_no_highlight , .Nm list_move_highlight , .Nm list_move_highlight_first , .Nm list_move_highlight_last , .Nm list_set_scrollbar , .Nm list_scroll , .Nm list_update , .Nm list_render , .Nm list_get_debug , .Nm list_set_debug , .Nm list_debug_to , .Nm list_panic , .Nm list_filter_always .Nd scrolling-list management .Sh SYNOPSIS .Fd #include .br .Nm cc .Op Ar arguments .Fl lscrolllist .br .Ft "LIST *" .Fn list_new "void *cookie" "void (*f)(void *, void *)" "int (*c)(void *, void *, void *)" "int (*l)(void *, void *)" "void (*r)(void *, void *, unsigned int, unsigned int, unsigned int)" "void (*d)(void *, void *, FILE *)" "void *e" .Ft void .Fn list_set_cookie "LIST *list" "void *cookie" .Ft void .Fn list_set_filter "LIST *list" "int (*f)(void *, void *)" .Ft void .Fn list_change_filter "LIST *list" "int (*f)(void *, void *)" .Ft void .Fn list_set_compare "LIST *list" "int (*c)(void *, void *, void *)" .Ft void .Fn list_change_compare "LIST *list" "int (*c)(void *, void *, void *)" .Ft void .Fn list_set_linecount "LIST *list" "int (*l)(void *, void *)" .Ft void .Fn list_change_linecount "LIST *list" "int (*l)(void *, void *)" .Ft void .Fn list_set_render "LIST *list" "int (*r)(void *, void *, unsigned int, unsigned int, unsigned int)" .Ft void .Fn list_set_empty_item "LIST *list" "void *ptr" .Ft void .Fn list_clear "LIST *list" .Ft void .Fn list_clear_cb "LIST *list" "void (*cb)(void *, void *)" .Ft void .Fn list_destroy "LIST *list" .Ft int .Fn list_add1 "LIST *list" "void *item" .Ft int .Fn list_addn "LIST *list" "void * const *items" "int nitems" "int *indices" .Ft void .Fn list_set_lines "LIST *list" "int nlines" .Ft int .Fn list_lines_wanted "LIST *list" .Ft int .Fn list_cur_top "LIST *list" .Ft int .Fn list_cur_hl_line "LIST *list" .Ft "void *" .Fn list_item_hl_ptr "LIST *list" .Ft int .Fn list_item_hl_inx "LIST *list" .Ft void .Fn list_set_hl_line "LIST *list" "int line" .Ft void .Fn list_set_hl_inx "LIST *list" "int inx" "int lwi" .Ft void .Fn list_set_no_highlight "LIST *list" .Ft void .Fn list_move_highlight "LIST *list" "int inc" .Ft void .Fn list_move_highlight_first "LIST *list" .Ft void .Fn list_move_highlight_last "LIST *list" .Ft void .Fn list_set_scrollbar "LIST *list" "void (*fn)(void *cookie, int nabove, int nshown, int nbelow)" "int nlines" .Ft void .Fn list_scroll "LIST *list" "int by" .Ft void .Fn list_update "LIST *list" .Ft void .Fn list_render "LIST *list" "int firstline" .Ft int .Fn list_get_debug "void" .Ft void .Fn list_set_debug "int enable" .Ft void .Fn list_debug_to "FILE *dbgto" .Ft void .Fn list_panic "LIST *l" .Ft int .Fn list_filter_always "void *cookie" "void *item" .Sh DESCRIPTION These functions manage scrolling lists of displayable items. Conceptually, each item is displayed on one or more screen lines. The list is capable of having a line highlighted, which can be used for user-interaction feedback. (Highlighting is optional.) The unit of display is the line; as implied above, an item may occupy more than one line. .Pp List items are represented in the API by void pointers. Each list also has a callback argument pointer, which is opaque to the library and is passed as the first argument to all application functions the library calls. Each item in a list also has an index; this is returned by the call which adds the item, and never changes until the item is removed from the list (which currently happens only when the list is cleared or destroyed). List indices are small nonnegative integers, packed relatively densely near zero (but not necessarily without gaps); it is, for example, reasonable for the application to use them as array subscripts, but is a bug to assume that adding a single item returns the lowest unused index. .Pp There are five function pointers associated with a list: .Pp .Bl -bullet -indent .It A filter function. The list as displayed can be only a subset of the items in the list; this determination is made by the filter function. .It A comparison function. The list displays items in sorted order; the order is determined by the comparison function. .It A line-count function. This is called to determine how many display lines a given item takes up. .It A render function. This is called to display a line. (In particular, this means that .Sq display is an application-specific concept.) .It A debug-print function. When debugging is on (see .Fn list_set_debug ) , this can be called to produce debugging descriptions. .El .Pp These functions are set at list creation time but can be changed later (except for the debug-print function, which there has been no call for run-time changing; this would be easy to fix). .Pp A list also has two pointers, opaque to the library. One was mentioned above; it is a callback argument, which is passed as the first argument to all callback functions (mostly the above five, but there are a few other APIs which involve callbacks). .Pp There are functions which change things about a list. Some of these are .Sq set functions; other are .Sq change functions. The difference is that calling a .Sq set function does nothing at all if the argument is the current value, whereas a .Sq change function always provokes any relevant recomputation, as if the argument were actually different. If you want to force recomputation without changing the function (for example, if you've changed other state which affects functions' return values), you can pass a nil pointer as the new function to a .Sq change call. .Pp The library is designed for lists larger than the number of display lines available. Of the list items which pass the filter, only a subset are displayed. Of the available lines (the lines corresponding to items which pass the filter), one is the top-of-screen line; this can be changed to scroll the display. .Pp The list is capable of displaying a scrollbar. By default, there is no bar. When enabled, the scrollbar is assumed to occupy a fixed number of lines; whether it is displayed at the top, the bottom, or somewhere else entirely is out of scope for the library. See .Fn list_set_scrollbar for more. .Sh FUNCTIONS .Bl -tag -width indent .It Fn list_new .Fn list_new creates a new list. It takes seven arguments: .Bl -tag -width indent .It "void *cookie" This is the callback cookie described above. It is opaque to the library and is passed as the first argument to all callbacks. .It "int (*filter)(void *cookie, void *item)" This is the list's filter function. Given the callback cookie and an item pointer, it returns nonzero if the item should be displayed or zero if not. .It "int (*compare)(void *cookie, void *a, void *b)" This is the list's comparison function. Given the callback cookie and two item pointers, it returns an integer indicating how the two items compare relative to one another for display purposes: less than zero if a should come before b, greater than zero if a should come after b, or zero if it doesn't matter which order they are displayed in. (The sort is not necessarily stable; if the comparison function returns zero for two items, they may switch display positions with one another unpredictably, which means this should usually be done only when they get displayed identically.) .It "int (*linecount)(void *cookie, void *item)" This is the list's line-count function. Given the callback cookie and an item pointer, it returns the number of display lines that item should occupy. It is an error for this to return a number less than 1. .It "void (*render)(void *cookie, void *item, unsigned int lwi, unsigned int dl, unsigned int flags)" This is the list's render function. It is called to render a line. (Note that the unit of rendering is the line, not the item, since items may occupy multiple lines, of which only some may be visible.) lwi is the line number within the item, from 0 through one less than what the line-count function returned for that item. dl is the display line; see .Fn list_render for further discussion of this. flags is flags: .Bl -tag -width indent .It Dv LIST_HIGHLIGHTED This bit is set iff the line should be displayed highlighted. (This will never be set if highlighting is disabled for the list. But it is not guaranteed that a line will be highlighted even if the list is in highlight mode; the commonest example is an empty list.) .El .Pp See also the noitem argument, below. .It "void (*debugprint)(void *cookie, void *item, FILE *printto)" When debugging is enable (see .Fn list_set_debug ) , this is called to print a debugging representation of the item to printto. .It "void *noitem" Sometimes (for example, when the sum of all items' line counts is less than the number of display lines available), the list may need to display lines that do not belong to any item. The render function is called as normal, but the item pointer argument cannot point to a normal item, because there is none. Instead, this pointer is passed as the item-pointer argument. (The line-within-item and flags arguments are always zero for such calls.) .El .Pp The return value, of course, is the new LIST pointer. .It Fn list_set_cookie This replaces the list's callback cookie. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "void *cookie" The new callback cookie pointer. .El .It Fn list_set_filter This replaces the list's filter function. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int (*filter)(void *cookie, void *item)" The new filter function. .El .Pp In terms of the set-versus-change discussion above, this is a set function. .It Fn list_change_filter This is just like .Fn list_set_filter , except it is a change function instead of a set function. .It Fn list_set_compare This replaces the list's compare function. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int (*compare)(void *cookie, void *a, void *b)" The new compare function. .El .Pp In terms of the set-versus-change discussion above, this is a set function. .It Fn list_change_compare This is just like .Fn list_set_compare , except it is a change function instead of a set function. .It Fn list_set_linecount This replaces the list's line-count function. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int (*linecount)(void *cookie, void *a, void *b)" The new line-count function. .El .Pp In terms of the set-versus-change discussion above, this is a set function. .It Fn list_change_linecount This is just like .Fn list_set_linecount , except it is a change function instead of a set function. .It Fn list_set_render This replaces the list's render function. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int (*render)(void *cookie, void *item, unsigned int lwi, unsigned int dl, unsigned int flags)" The new render function. .El .Pp The set-versus-change discussion above does not apply here, because there is no recomputation to be done based on the render function. .It Fn list_set_empty_item This replaces the list's no-item pointer. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "void *ptr" The new no-item pointer. .El .Pp The set-versus-change discussion above does not apply here, because there is no recomputation to be done based on the no-item pointer. .It Fn list_clear Clears a list, removing all items from it. The argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_clear_cb Clears a list, removing all items from it, calling a callback for each one. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "void (*cb)(void *cookie, void *item)" The callback. It gets the usual callback cookie and the item pointer. .El .Pp No promises are made about what order the items' callbacks are called in. .It Fn list_destroy Destroys a list. This does the equivalent of .Fn list_clear on the list, then tears it down completely. (If you want a callback for the clear operation, call .Fn list_clear_cb before calling .Fn list_destroy . ) Argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_add1 Adds a single new item to the list. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "void *item" The item. .El .Pp The return value is the item's index. .It Fn list_addn Adds potentially multiple items to the list. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "void * const *items" The vector of items. .It "int nitems" The number of items. .It "int *indices" Pointer to a vector of ints to receive the new items' indices. It must point to at least nitems ints, and, of course, elements of items and indices correspond to one another. .El .It Fn list_set_lines Sets the number of display lines available to the list. The arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int nlines" The number of display lines available. .El .Pp It is an error for nlines to be less than two. .It Fn list_lines_wanted Returns the number of lines a list wants. To put it another way, this is the number of item lines the list would occupy if it had an infinite number of screen lines available to it. It is the sum of the line-count values for all the list's items which pass the filter. The argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_cur_top Returns the line which is currently at top-of-screen. The argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .Pp If the list is empty, so there is no .Sq line at the top-of-screen , the return value is 0. .Pp .It Fn list_cur_hl_line If the list is in no-highlight mode, returns -1. Otherwise, if the list is empty, returns 0. Otherwise, returns the line which is currently highlighted, as a line number from 0 through .Fn list_lines_wanted No Ns -1. As this implies, the return value is independent of scrolling. Argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_item_hl_ptr It is an error to call this on a list which is in no-highlight mode. Otherwise, if the list is empty, returns nil. Otherwise, returns the item containing the highlighted line, as an item pointer. Argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_item_hl_inx It is an error to call this on a list which is in no-highlight mode. Otherwise, if the list is empty, returns -1. Otherwise, returns the item containing the highlighted line, as an item index. Argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_set_hl_line Sets the highlighted line, where the line is a number from 0 through .Fn list_lines_wanted No Ns -1. If the list is in no-highlight mode, this takes it out of no-highlight mode; if the list is empty, it does nothing else. If the line number is too positive or too negative, it is clamped to the valid range. Arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int line" The to-be-highlighted line's number. .El .It Fn list_set_hl_inx Sets the highlighted line, described as an item index and a line-within-item. It is an error for the item index to be invalid or for the line-within-item number to be out of range for the item (ie, negative, or \&>= the value returned by the list's line-count function for that item). As this implies, there is no such thing as a valid call to this function for an empty list (because an empty list has no valid index values). Arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int inx" The to-be-highlighted line's item's index. .It "int lwi" The to-be-highlighted line's number relative to its item's first line. .El .It Fn list_set_no_highlight Puts the list in no-highlight mode. Argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_move_highlight Moves the highlight, or, for no-highlight lists, scrolls. Arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int inc" The offset. .El .Pp If the list is in highlight mode, this moves the highlight by inc, where a positive increment moves it down the list and negative moves it up, except it never moves it past the relevant end of the list. If the list is in no-highlight mode, scrolls the list, adding inc to the top-of-screen line number (again, clamping the result to valid range). In either case, this can cause the list to scroll. .Pp If the list is empty, this does nothing. .It Fn list_move_highlight_first This is just like .Fn list_move_highlight on the same list, with an integer argument so negative that it moves as far as it can. Argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_move_highlight_last This is just like .Fn list_move_highlight on the same list, with an integer argument so positive that it moves as far as it can. Argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fl list_set_scrollbar This controls the list's scrollbar. Arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "void (*fn)(void *cookie, int nabove, int nshown, int nbelow)" The scrollbar rendering function. .It "int nlines" The number of lines the scrollbar takes up, when it is displayed. .El .Pp If fn is nil, nlines is ignored, and the list is put in no-scrollbar mode. (No-scrollbar mode is the default for a new list.) .Pp Otherwise, fn is called during .Fn list_render to render the scrollbar. If no scrollbar should be displayed, then nabove, nshown, and nbelow are each -1. Otherwise, nabove is the number of lines not displayed above the displayed lines, nshown is the number of displayed lines, and nbelow is the number of lines not displayed below the displayed lines. In this case, nlines is the number of lines the scrollbar, when displayed, occupies. .It Fn list_scroll Scrolls the list. Arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int scrollby" The number of lines to scroll by. .El For a no-highlight list, this is operationally equivalent to .Fn list_move_highlight . But, for a list in highlight mode, this always scrolls the list, clamping it at the beginning and end of the available lines, moving the highlight as necessary to keep it visible. (The difference is that, for highlight-mode lists, .Fn list_move_highlight moves the highlight as directed, scrolling when necessary, whereas .Fn list_scroll scrolls as directed, moving the highlight when necessary.) Except for clipping, the argument is effecctively added to the top-of-screen line number. .Pp If the list is empty, this does nothing. .It Fn list_update Updates the list's internal data structures. This is necessary to get correct values from inquiry functions such as .Fn list_lines_wanted . It is cheap to call this if nothing relevant has been changed since the last update. The equivalent of .Fn list_update is performed by .Fn list_render . The argument is: .Bl -tag -width indent .It "LIST *list" The affected list. .El .It Fn list_render Redraws the list. Arguments are: .Bl -tag -width indent .It "LIST *list" The affected list. .It "int firstline" The display line number of the first line available to the list. .El .Pp This first performs the equivalent of .Fn list_update . It then calls the list's render function for each line on the screen. The firstline argument is not, strictly, necessary; it is simply an offset added to each display line before passing it to the render function. This makes it slightly easier to render a list in part of a larger window. For example, if we have 20 lines available and want the list to use all but the first two and last three of them, the appropriate calls would be .D1 list_set_lines(list,15); .D1 list_render(list,2); and the list's render function would be called with display line numbers 2 through 16. .Pp Nothing is promised about what order the screen lines are redrawn in. .It Fn list_get_debug Returns the debug setting. Returns zero if debugging is off, nonzero if it's on. See .Fn list_set_debug , below, for more. No arguments. .It Fn list_set_debug Turns debugging on or off. The default, of course, is off. Note that there is only one debugging setting for the whole library, not one per list. See also .Fn list_debug_to , below. The argument is: .Bl -tag -width indent .It "int enable" Zero to disable debugging, nonzero to enable it. .El .Pp This is designed for debugging the library. As such, it assumes you have access to, and are comfortable using, the source code, so the details are not documented here. .It Fn list_debug_to Turns on debugging, like .Fn list_set_debug with a nonzero argument, but also sets the destination for the debugging output. See also .Fn list_set_debug . .Bl -tag -width indent .It "FILE *dbgto" The standard I/O stream debugging output should go to. .El .It Fn list_panic This routine is a bit special. It is not an API for the application to call. Rather, the library calls this if it ever hits a bugcheck. If the application provides such a function, it will get called; if not, or if the application-provided version returns, the library will call .Xr abort 3 . The function's argument list is: .Bl -tag -width indent .It "LIST *l" The list being manipulated when the internal error was discovered. .El .Pp If this is ever called, the affected list is not usable. The only reasonable way for the application to continue running is to throw out (longjmp, a gcc nonlocal goto, etc) and never do anything further at all with the relevant list, including destroying it. It exists mostly for applications which want to crash more gracefully than a simple .Xr abort 3 . .It Fn list_filter_always This is a convenience routine provided by the library for applications that aren't interested in ever filtering any items. It ignores its arguments and always returns nonzero. a filter function .El .Sh BUGS Probably. .Sh AUTHOR der Mouse, .Aq mouse@rodents-montreal.org .