This chapter describes another control that the user can manipulate. The List widget displays a number of text choices that the user can select interactively.
Almost every application needs to display lists of choices to the user. This task can be accomplished in many ways, depending on the nature of the choices. For example, a group of ToggleButtons is ideal for displaying configuration settings that can be individually set and unset and then applied all at once. A list of commands can be displayed in a PopupMenu, or for a more permanent command palette, a RowColumn or Form widget can manage a group of PushButton widgets. But for displaying a list of text choices, such as a list of files to be opened or a list of fonts to be applied to text, nothing beats a List widget.
A List widget displays a single column of text choices that can be selected or deselected using either the mouse or the keyboard. Each choice is represented by a single-line text element specified as a compound string. the figure shows a typical List widget.

Internally, the List widget operates on an array of compound strings that are defined by the application. (See Chapter 19, Compound Strings, for a discussion of how to create and manage compound strings.) Compound strings that use multiple fonts are allowed, but the List widget does not render these items very well. Each string is an element of the array, with the first position starting at one, as opposed to position zero, which is used in C-style arrays. The user can select a particular choice by clicking and releasing the left mouse button on the item. All of the items in the list are available to the user for selection at all times; you cannot make individual items unselectable. What happens when an item is selected is up to the application callback routines invoked by the List widget.
A List widget is typically a child of a ScrolledWindow, so that the List is displayed with ScrollBars attached to it. The selection mechanism for the List does not change, so the user can still select items as before, but the user can now use the ScrollBars to adjust the items in the list that are visible.
The List widget supports four different selection policies:
Using List widgets is fairly straightforward. An application that uses the List widget must include the header file <Xm/List.h>. This header file declares the types of the public List functions and the widget class namexmListWidgetClass. A List widget can be created as shown in the following code fragment:
Widget list; list = XtVaCreateManagedWidget ("name", xmListWidgetClass, parent,resource-value-list, NULL); the source code shows a program that creates a simple List widget.XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4.XmStringCreateLocalized() is only available in Motif 1.2;XmStringCreateSimple() is the corresponding function in Motif 1.1. /* simple_list.c -- introduce the List widget. Lists present * a number of comound strings as choices. Therefore, strings * must be converted before set in lists. Also, the number of * visible items must be set or the List defaults to 1 item. */ #include <Xm/List.h> char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; main(argc, argv) int argc; char *argv[]; { Widget toplevel; XtAppContext app; int i, n = XtNumber (months); XmStringTable str_list; XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); str_list = (XmStringTable) XtMalloc (n * sizeof (XmString)); for (i = 0; i < n; i++) str_list[i] = XmStringCreateLocalized (months[i]); XtVaCreateManagedWidget ("Hello", xmListWidgetClass, toplevel, XmNvisibleItemCount, n, XmNitemCount, n, XmNitems, str_list, NULL); for (i = 0; i < n; i++) XmStringFree (str_list[i]); XtFree (str_list); XtRealizeWidget (toplevel); XtAppMainLoop (app); } The program simply creates a List widget as the child of thetoplevel widget. The List contains the names of the months as its choices. The output of the program is shown in the figure.
The selection policy of the List is controlled by theXmNselectionPolicy resource. The possible values for this resource are:
XmSINGLE_SELECT XmBROWSE_SELECT XmMULTIPLE_SELECT XmEXTENDED_SELECTXmBROWSE_SELECT is the default selection policy for the List widget. Since this policy is the one that we want to use, we do not need to set theXmNselectionPolicy resource. You should be aware that the user could change this policy with a resource specification. If you want to enforce this selection policy, you can program defensively and hard-code the value forXmNselectionPolicy, despite its default.
The program demonstrates the use of three basic elements of the List widget: the list of items, the number of items in the list, and the number of visible items. Because the items in a List must be compound strings, each of the choices must be converted from a C string to a compound string. The application allocates an array ofXmStrings, creates a compound string for each month name, and stores the string in thestr_list. The List widget is created withstr_list as the value for theXmNitems resource andXmNitemCount is set ton.
Just like other widgets that use compound strings, the List widget copies the entire table of compound strings into its own internal storage. As a result, the list of strings needs to be freed after you have used it to set theXmNitems resource. When you set the items using this resource, you also need to set theXmNitemCount resource to specify the number of items in the list. If this resource is not set, the List does not know how many items to copy. The value ofXmNitemCount should never be larger than the number of items inXmNitems. If the value forXmNitemCount is less than the number of items, the additional items are not put in the list.
To retrieve the list of items, you can callXtVaGetValues() on these resources, as shown in the following code fragment:
extern Widget list; XmStringTable choices; int n_choices; XtVaGetValues (list, XmNitems, &choices, XmNitemCount, &n_choices, NULL);Since the items that the area returned are compound strings, you must convert them to C-style strings if you need to use any of the standard C library functions to view or manipulate the strings. You can also use any of the compound string functions described in Chapter 19, Compound Strings, for this purpose. Since we usedXtVaGetValues() to obtain the values for the resources, the returned data should, as always, be considered read-only. You should not change any of the items in the list or attempt to free them (or the pointer to them) when you are done examining their values.
the source code also makes use of theXmNvisibleItemCount resource, which sets the height of the list to match the number of items that should be visible. If you want all the items to be visible, you simply set the value to the total number of items in the list. Setting the visible item count to a higher value is acceptable, assuming that the list is expected to grow to at least that size. If you want to set the number of visible items to be less than the number of items actually in the list, you should use a ScrolledList as described in the next section.
Most applications use List widgets in conjunction with ScrolledWindows. By creating a List widget as the child of a ScrolledWindow, we create what Motif calls a ScrolledList. The ScrolledList is not a widget, but a compound object. While this chapter describes most of the common resources and functions that deal with ScrolledLists, more detailed information about ScrolledWindows and ScrollBars can be found in Chapter 9, ScrolledWindows and ScrollBars.
A ScrolledList is built from two widget classes, so we could create and manage the widgets separately using two calls toXtVaCreateManagedWidget(). However, since ScrolledLists are used so frequently, Motif provides a convenience function to create this compound object.XmCreateScrolledList() takes the following form:
Widget XmCreateScrolledList(parent,name,arglist,argcount) Widgetparent; char *name; ArgListarglist; Cardinalargcount;Thearglist parameter is an array of sizeargcount that contains resources to be passed to both the ScrolledWindow widget and the List widget. Generally, the two widgets use different resources that are specific to the widgets themselves, so there isn't any confusion about which resources apply to which widget. However, common resources, such as Core resources, are interpreted by both widgets, so caution is advised. If you want to set some resources on one widget, while ensuring that the values are not set on the other widget, you should avoid passing the values to the convenience routine. Instead, you can set resources separately by usingXtVaSetValues() on each widget individually.XmCreateScrolledList() returns the List widget; if you need a handle to the ScrolledWindow, you can useXtParent() on the List widget. When you use the convenience routine, you need to manage the object explicitly withXtManageChild().
ScrolledLists are useful because they can display a portion of the entire list provided by the widget. For example, we can modify the previous example, simple_list.c, to use a ScrolledList by using the following code fragment:
... /* Create the ScrolledList */ list_w = XmCreateScrolledList (toplevel, "Months", NULL, 0); /* set the items, the item count, and the visible items */ XtVaSetValues (list_w, XmNitems, str_list, XmNitemCount, n, XmNvisibleItemCount, 5, NULL); /* Convenience routines don't create managed children */ XtManageChild (list_w); ...The size of the viewport into the entire List widget is controlled by theXmNvisibleItemCount resource. In Motif 1.1, the value of this resource defaults to1, while in Motif 1.2, the resource calculates its value based on theXmNheight of the List. We set the resource to5. The output resulting from our changes is shown in the figure.
TheXmNscrollBarDisplayPolicy andXmNlistSizePolicy resources control the display of the ScrollBars in a ScrolledList. The value forXmNscrollBarDisplayPolicy controls the display of the vertical ScrollBar; the resource can be set to eitherXmAS_NEEDED (the default) orXmSTATIC. If the policy isXmAS_NEEDED, when the entire list is visible, the vertical ScrollBar is not displayed. When the resource is set toXmSTATIC, the vertical ScrollBar is always displayed. TheXmNlistSizePolicy resource reflects

how the ScrolledList manages its horizontal ScrollBar. The default setting isXmVARIABLE, which means that the ScrolledList attempts to grow horizontally to contain its widest item and a horizontal ScrollBar is not displayed. This policy may present a problem if the parent of the ScrolledList constrains its horizontal size. If the resource is set toXmRESIZE_IF_ POSSIBLE, the ScrolledList displays a horizontal ScrollBar only if it cannot resize itself accordingly. If the valueXmCONSTANT is used, the horizontal ScrollBar is displayed at all times, whether it is needed or not.
The size of a ScrolledList is ultimately controlled by its parent. In most cases, a manager widget such as a RowColumn or Form allows its children to be any size they request. If a ScrolledList is a child of a Form widget, its size is whatever you specify with either theXmNheight resource or theXmNvisibleItemCount. However, certain constraints, such as theXmNresizePolicy in a Form widget, may affect the height of its children unexpectedly. For example, if you setXmNresizePolicy toXmRESIZE_NONE, the ScrolledList widget's height request is ignored, which makes it look likeXmNvisibleItemCount is not working.
The List widget accepts keyboard input to select items in the list, browse the list, and scroll the list. Like all other Motif widgets, the List has translation functions that facilitate this process. The translations are hard-coded into the widget and we do not recommend attempting to override this list with new translations. For ScrolledLists, the List widget automatically sets the ScrollBar'sXmNtraversalOn resource toFalse so that the ScrollBar associated with the ScrolledList does not get keyboard input. Instead, the List widget handles the input that affects scrolling. We recommended that you do not interfere with this process, so users are not confused by different applications on the desktop behaving in different ways.
If a List widget is sensitive, all of the items in the List are selectable. If it is insensitive, none of them are selectable. You cannot set certain items to be insensitive to selection at any given time. Furthermore, you cannot set the entire List to be insensitive and allow the user to manipulate the ScrollBars. It is not entirely possible to make a read-only List widget; the user always has the ability to select items in the List, providing that it is sensitive. Of course, you can always choose not to hook up callback procedures to the widget, but this can lead to more confusion than anything else because if the user selects an object and the toolkit provides the visual feedback acknowledging the action, the user will expect the application to respond as well.
From the programmer's perspective, much of the power of the List widget comes from being able to manipulate its items. The toolkit provides a number of convenience functions for dealing with the items in a List. While the items are accesible through theXmNitems resource, the convenience routines are designed to deal with many common operations, such as adding items to the List, removing items, and locating items.
The entire list of choices may not always be available at the time the List is created. In fact, it is not uncommon to have no items available for a new list. In these situations, items can be added to the list dynamically using the following functionsXmListAddItem(),XmListAddItemUnselected(),XmListAddItems(), andXmListAddItemsUnselected().XmListAddItemsUnselected() is a new routine in Motif 1.2. These functions take the following form:
void XmListAddItem(list_w,item,position) Widgetlist_w; XmStringitem; intposition;
void XmListAddItemUnselected(list_w,item,position) Widgetlist_w; XmStringitem; intposition;
void XmListAddItems(list_w,items,item_count,position) Widgetlist_w; XmString *items; intitem_count; intposition;
void XmListAddItemsUnselected(list_w,items,item_count,position) Widgetlist_w; XmString *items; intitem_count; intposition;
These routines allow you to add one or more items to a List widget at a specified position. Remember that list positions start at1, not0. The position0 indicates the last position in the List; specifying this position appends the item or items to the end of the list. If the new item(s) are added to the list in between existing items, the rest of the items are moved down the list.
The difference betweenXmListAddItem() andXmListAddItemUnselected() is thatXmListAddItem() compares each new item to each of the existing items. If a new item matches an existing item and if the existing item is selected, the new item is also selected.XmListAddItemUnselected() simply adds the new item without performing this check. In most situations, it is clear which routine you should use. If you know that the new item does not already exist, you should add it unselected. If the List is a single selection list, you should add new items as unselected. The only time that you should really add new items to the list usingXmListAddItem() is when there could be duplicate entries, the list supports multiple selections, and you explicitly want to select all new items whose duplicates are already selected. The same is true of the routines that add multiple items.
the source code shows how items can be added to a ScrolledList dynamically usingXmListAddItemUnselected().XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4.XmStringCreateLocalized() is only available in Motif 1.2;XmStringCreateSimple() is the corresponding function in Motif 1.1.XmFONTLIST_DEFAULT_TAG replacesXmSTRING_DEFAULT_CHARSET in Motif 1.2.
/* alpha_list.c -- insert items into a list in alphabetical order. */ #include <Xm/List.h> #include <Xm/RowColumn.h> #include <Xm/TextF.h> main(argc, argv) int argc; char *argv[]; { Widget toplevel, rowcol, list_w, text_w; XtAppContext app; Arg args[5]; int n = 0; void add_item(); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); rowcol = XtVaCreateWidget ("rowcol", xmRowColumnWidgetClass, toplevel, NULL); XtSetArg (args[n], XmNvisibleItemCount, 5); n++; list_w = XmCreateScrolledList (rowcol, "scrolled_list", args, n); XtManageChild (list_w); text_w = XtVaCreateManagedWidget ("text", xmTextFieldWidgetClass, rowcol, XmNcolumns, 25, NULL); XtAddCallback (text_w, XmNactivateCallback, add_item, list_w); XtManageChild (rowcol); XtRealizeWidget (toplevel); XtAppMainLoop (app); } /* Add item to the list in alphabetical order. Perform binary * search to find the correct location for the new item position. * This is the callback routine for the TextField widget. */ void add_item(text_w, client_data, call_data) Widget text_w; XtPointer client_data; XtPointer call_data; { Widget list_w = (Widget) client_data; char *text, *newtext = XmTextFieldGetString (text_w); XmString str, *strlist; int u_bound, l_bound = 0; /* newtext is the text typed in the TextField widget */ if (!newtext || !*newtext) { /* non-null strings must be entered */ XtFree (newtext); /* XtFree() checks for NULL */ return; } /* get the current entries (and number of entries) from the List */ XtVaGetValues (list_w, XmNitemCount, &u_bound, XmNitems, &strlist, NULL); u_bound--; /* perform binary search */ while (u_bound >= l_bound) { int i = l_bound + (u_bound - l_bound) / 2; /* convert the compound string into a regular C string */ if (!XmStringGetLtoR (strlist[i], XmFONTLIST_DEFAULT_TAG, &text)) break; if (strcmp (text, newtext) > 0) u_bound = i - 1; /* newtext comes before item */ else l_bound = i + 1; /* newtext comes after item */ XtFree (text); /* XmStringGetLtoR() allocates memory ... yuk */ } str = XmStringCreateLocalized (newtext); XtFree (newtext); /* positions indexes start at 1, so increment accordingly */ XmListAddItemUnselected (list_w, str, l_bound+1); XmStringFree (str); XmTextFieldSetString (text_w, ""); }In the source code the ScrolledList is created with no items. However, we do specifyXmNvisibleItemCount, in anticipation of items being added to the list. A TextField widget is used to prompt for strings that are added to the list using theadd_item() callback. This function performs a binary search on the list to determine the position where the new item is to be added. A binary search can save time, as it is expensive to scan an entire List widget and convert each compound string into a C string. When the position for the new item is found, it is added usingXmListAddItemUnselected(). The output of this program is shown in the figure.

It is often useful to be able to determine whether or not a List contains a particular item. The simplest function for determining whether a particular item exists isXmListItemExists(), which takes the following form:
Boolean XmListItemExists(list_w,item) Widgetlist_w; XmStringitem;This function performs a linear search on the list for the specified item. If you are maintaining your list in a particular order, you may want to search the list yourself using another type of search to improve performance. The List's internal search function does not convert the compound strings to C strings. The search routine does a direct byte-by-byte comparison of the strings usingXmStringByteCompare(), which is much more efficient than converting the compound strings to C strings for comparison. However, the linear search is still slower than a binary search by orders of magnitude. And unfortunately,XmStringByteCompare() does not return which string is of greater or lesser value. The routine just returns whether the strings are different, so we cannot use it to alphabetize the items in a List.
If you need to know the position of an item in the List, you can useXmListItemPos(). This routine takes the following form:
int XmListItemPos(list_w,item) Widgetlist_w; XmStringitem;This function returns the position of the first occurrence ofitem in the List, with1 being the first position. If the function returns0, the element is not in the List. If a List contains duplicate entries, you can find all of the positions of a particular item usingXmListGetMatchPos(), which takes the following form:
Boolean XmListGetMatchPos(list_w,item,pos_list,pos_cnt) Widgetlist_w; XmStringitem; int **pos_list; int *pos_cnt;This function returnsTrue if the specified item is found in the List in one or more locations. Thepos_list parameter is allocated to contain the array of positions of the item and the number of items found is returned inpos_cnt. When you are done usingpos_list, you should free it usingXtFree(). The function returnsFalse if there are no items in the List, if memory cannot be allocated forpos_list, or if the specified item isn't in the List. In these cases,pos_list does not point to allocated space and should not be referenced or freed and the value ofpos_cnt is not specified. The following code fragment shows the use ofXmListGetMatchPos() to get the positions of an item in a List:
extern Widget list_w; int *pos_list; int pos_cnt, i; char *choice = "A Sample Text String"; XmString str = XmStringCreateLocalized (choice); if (!XmListGetMatchPos (list_w, str, &pos_list, &pos_cnt)) XtWarning ("Can't get items in list"); else { printf ("%s exists in positions %d:", choice, pos_cnt); for (i = 0; i < pos_cnt; i++) printf (" %d", pos_list[i]); puts (""); XtFree (pos_list); } There are also a number of functions for replacing items in a List. To replace a contiguous sequence of items, use eitherXmListReplaceItemsPos() orXmListReplaceItemsPosUnselected(). These functions take the following form:
void XmListReplaceItemsPos(list_w,new_items,item_count,position) Widgetlist_w; XmString *new_items; intitem_count; intposition; void XmListReplaceItemsPosUnselected(list_w,new_items,item_count,position) Widgetlist_w; XmString *new_items; intitem_count; intposition;These functions replace the specified number of items with the new items starting atposition. The difference between the two functions is the same as the difference between the List routines that add items selected and unselected.XmListReplaceItemsPosUnselected() is a new routine in Motif 1.2.
You can also replace arbitrary elements in the list with new elements, usingXmListReplaceItems() orXmListReplaceItemsUnselected. These routines take the following form:
void XmListReplaceItems(list_w,old_items,item_count,new_items) Widgetlist_w; XmString *old_items; intitem_count; XmString *new_items;
void XmListReplaceItemsUnselected(list_w,old_items,item_count,new_items) Widgetlist_w; XmString *old_items; intitem_count; XmString *new_items;These functions work by searching the entire list for each element inold_items. Every occurrence of each element that is found is replaced with the corresponding element fromnew_items. The search continues for each element inold_items untilitem_count has been reached. The difference between the two functions is the same as the difference between the List routines that add items selected and unselected.XmListReplaceItemsUnselected() is a new routine in Motif 1.2.
There is another new routine in Motif 1.2 that allows you to replace items in a List based upon position. TheXmListReplacePositions() routine takes the following form:
void XmListReplacePositions(list_w,pos_list,new_items,item_count) Widgetlist_w; int *pos_list; XmString *new_items; intitem_count;This routine replaces the item at each position specified inpos_list with the corresponding item innew_items untilitem_count has been reached.
You can delete items from a List widget in many ways. First, to delete a single item, you can use eitherXmListDeleteItem() orXmListDeletePos(). These functions take the following form:
void XmListDeleteItem(list_w,item) Widgetlist_w; XmStringitem;
void XmListDeletePos(list_w,position) Widgetlist_w; intposition;XmListDeleteItem() finds the given item and deletes it from the list, whileXmListDeletePos() removes an item directly from the given position. If you know the position of an item, you can avoid creating a compound string and useXmListDeletePos(). After an item is deleted, the items following it are moved up one position.
You can delete multiple items using eitherXmListDeleteItems(),XmListDeleteItemsPos(), orXmListDeletePositions(). These routines take the following form:
void XmListDeleteItems(list_w,items,item_count) Widgetlist_w; XmString *items; intitem_count;
XmListDeleteItemsPos(list_w,item_count,position) Widgetlist_w; intitem_count; intposition;
XmListDeletePositions(list_w,pos_list,pos_count) Widgetlist_w; int *pos_list; intpos_count;XmListDeleteItems() deletes each of the items in theitems array from the List; there areitem_count strings in the array. You must create and initialize this array before calling the function and you must free it afterwards. If you already know the positions of the items you want to delete, you can avoid creating an array of compound strings and useXmListDeleteItemsPos() orXmListDeletePositions().XmListDeleteItemsPos() deletesitem_count items from the List starting atposition.XmListDeletePositions() deletes the item at each position specified inpos_list untilitem_count has been reached. This routine is new in Motif 1.2.
You can delete all of the items in a List widget usingXmListDeleteAllItems(). This routine takes the following form:
void XmListDeleteAllItems(list_w) Widgetlist_w;
Since the main purpose of the List widget is to allow a user to make a selection from a set of choices, one of the most important tasks for the programmer is to determine which items have been selected by the user. In this section, we present an overview of the resources and functions available to set or get the actual items that are selected in the List widget. Later in Section #slistcb, we discuss how to determine the items that are selected by the user when they are selected. The resources and functions used to set and get the selected items in the List widget are directly analogous to those that set the actual items in the list. Just asXmNitems represents the entire list, theXmNselectedItems resource represents the list of selected items. TheXmNselectedItemCount resource specifies the number of items that are selected.
There are convenience routines that allow you to modify the items that are selected in a List. The functionsXmSelectItem() andXmSelectPos() can be used to select individual items. These functions take the following form:
void XmListSelectItem(list_w,item,notify) Widgetlist_w; XmStringitem; Booleannotify;
void XmListSelectPos(list_w,position,notify) Widgetlist_w; intposition; Booleannotify;These functions cause the specified item to be selected. If you know the position in the list of the item to be selected, you should useXmListSelectPos() rather thanXmListSelectItem(). The latter routine uses a linear search to find the specified item. The search can take a long time in a large list, which can affect performance if you are performing frequent list operations.
When the specified item is selected, any other items that have been previously selected are deselected, except whenXmNselectionPolicy is set toXmMULTIPLE_SELECT. In this case, the specified item is added to the list of selected items. Even though the extended selection policy allows multiple items to be selected, the previous selection is deselected when one of these routines is called. If you want to add an item to the list of selected items in an extended selection list, you can set the selection policy toXmMULTIPLE_SELECT, use one of the routines, and then set the selection policy back toXmEXTENDED_SELECT.
Thenotify parameter indicates whether or not the callback routine for the List widget should be called. If your callback routine does special processing of list items, then you can avoid having redundant code by passingTrue. As a result, the callback routine is called just as if the user had made the selection himself. If you are calling either of these functions from the callback routine, you probably want to passFalse to avoid a possible infinite loop.
There are no functions available for selecting multiple items at the same time. To select multiple items, useXtVaSetValues() and set theXmNselectedItems andXmNselectedItemCount resources to the entire list of selected items. Another alternative is to follow the suggestion made earlier and temporarily setXmNselectionPolicy toXmMULTIPLE_SELECT. You can call the above routines repeatedly to select the desired items individually and then set the selection policy back toXmEXTENDED_SELECT.
Items can be deselected in the same manner that they are selected usingXmListDeselectItem() andXmListDeselectPos(). These functions take the following form:
void XmListDeselectItem(list_w,item) Widgetlist_w; XmStringitem;
void XmListDeselectPos(list_w,position) Widgetlist_w; intposition;These routines modify the list of selected items, but they do not have anotify parameter, so they do not invoke the callback routine for the List. You can deselect all items in the list by callingXmListDeselectAllItems(), which takes the following form:
void XmListDeselectAllItems(list_w) Widgetlist_w;
There are also convenience routines that allow you to check on the selected items in a List. You can useXmListPosSelected() to determine whether an item is selected. This routine in new in Motif 1.2; it takes the following form:
Boolean XmListPosSelected(list_w,position) Widgetlist_w; intposition;The routine returnsTrue if the item at the specified position is selected andFalse otherwise. You can get the positions of all of the selected items in a List usingXmListGetSelectedPos(), which takes the following form:
Boolean XmListGetSelectedPos(list_w,pos_list,pos_cnt) Widgetlist_w; int **pos_list; int *pos_cnt;The use of this function is identical to that ofXmListGetMatchPos(). Thepos_list parameter is allocated to contain the array of positions of selected items and the number of items selected is returned inpos_cnt. When you are done usingpos_list, you should free it usingXtFree(). The function returnsFalse if there are no selected items in the List or if memory cannot be allocated forpos_list. In these cases,pos_list does not point to allocated space and should not be referenced or freed and the value ofpos_cnt is not specified.
In this section, we pull together all of the functions we have described in the preceding sections. This example builds on alpha_list.c, the program that adds items that are input by the user to a ScrolledList in alphabetical order. Using another Text widget, the user can also search for items in the list. The searching method uses regular expression pattern-matching functions intrinsic to UNIX systems. the source code shows the new application.XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4.XmStringCreateLocalized() is only available in Motif 1.2;XmStringCreateSimple() is the corresponding function in Motif 1.1.XmFONTLIST_DEFAULT_TAG replacesXmSTRING_DEFAULT_CHARSET in Motif 1.2.
/* search_list.c -- search for items in a List and select them */ #include <stdio.h> #include <Xm/List.h> #include <Xm/LabelG.h> #include <Xm/Label.h> #include <Xm/RowColumn.h> #include <Xm/PanedW.h> #include <Xm/TextF.h> main(argc, argv) int argc; char *argv[]; { Widget toplevel, rowcol, list_w, text_w; XtAppContext app; Arg args[5]; int n = 0; XmString label; void add_item(), search_item(); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); rowcol = XtVaCreateWidget ("rowcol", xmPanedWindowWidgetClass, toplevel, NULL); label = XmStringCreateLocalized ("List:"); XtVaCreateManagedWidget ("list_lable", xmLabelWidgetClass, rowcol, XmNlabelString, label, NULL); XmStringFree (label); XtSetArg (args[n], XmNvisibleItemCount, 10); n++; XtSetArg (args[n], XmNselectionPolicy, XmEXTENDED_SELECT); n++; list_w = XmCreateScrolledList (rowcol, "scrolled_list", args, n); XtManageChild (list_w); label = XmStringCreateLocalized ("Add:"); XtVaCreateManagedWidget ("add_label", xmLabelWidgetClass, rowcol, XmNlabelString, label, NULL); XmStringFree (label); text_w = XtVaCreateManagedWidget ("add_text", xmTextFieldWidgetClass, rowcol, XmNcolumns, 25, NULL); XtAddCallback (text_w, XmNactivateCallback, add_item, list_w); label = XmStringCreateLocalized ("Search:"); XtVaCreateManagedWidget ("search_label", xmLabelWidgetClass, rowcol, XmNlabelString, label, NULL); XmStringFree (label); text_w = XtVaCreateManagedWidget ("search_text", xmTextFieldWidgetClass, rowcol, XmNcolumns, 25, NULL); XtAddCallback (text_w, XmNactivateCallback, search_item, list_w); XtManageChild (rowcol); XtRealizeWidget (toplevel); XtAppMainLoop (app); } /* Add item to the list in alphabetical order. Perform binary * search to find the correct location for the new item position. * This is the callback routine for the Add: TextField widget. */ void add_item(text_w, client_data, call_data) Widget text_w; XtPointer client_data; XtPointer call_data; { Widget list_w = (Widget) client_data; char *text, *newtext = XmTextFieldGetString (text_w); XmString str, *strlist; int u_bound, l_bound = 0; if (!newtext || !*newtext) { /* non-null strings must be entered */ XtFree (newtext); return; } XtVaGetValues (list_w, XmNitemCount, &u_bound, XmNitems, &strlist, NULL); u_bound--; /* perform binary search */ while (u_bound >= l_bound) { int i = l_bound + (u_bound - l_bound)/2; if (!XmStringGetLtoR (strlist[i], XmFONTLIST_DEFAULT_TAG, &text)) break; if (strcmp (text, newtext) > 0) u_bound = i-1; /* newtext comes before item */ else l_bound = i+1; /* newtext comes after item */ XtFree (text); } str = XmStringCreateLocalized (newtext); XtFree (newtext); /* positions indexes start at 1, so increment accordingly */ XmListAddItemUnselected (list_w, str, l_bound+1); XmStringFree (str); XmTextFieldSetString (text_w, ""); } /* find the item in the list that matches the specified pattern */ void search_item(text_w, client_data, call_data) Widget text_w; XtPointer client_data; XtPointer call_data; { Widget list_w = (Widget) client_data; char *exp, *text, *newtext = XmTextFieldGetString (text_w); XmString *strlist, *selectlist = NULL; int matched, cnt, j = 0; #ifndef SYSV extern char *re_comp(); #endif /* SYSV */ if (!newtext || !*newtext) { /* non-null strings must be entered */ XtFree (newtext); return; } /* compile expression into pattern matching library */ #ifdef SYSV if (!(exp = regcmp (newtext, NULL))) { printf ("Error with regcmp(%s)0, newtext); XtFree (newtext); return; } #else /* BSD */ if (exp = re_comp (newtext)) { printf ("Error with re_comp(%s): %s0, newtext, exp); XtFree (newtext); return; } #endif /* SYSV */ /* get all the items in the list ... we're going to search each one */ XtVaGetValues (list_w, XmNitemCount, &cnt, XmNitems, &strlist, NULL); while (cnt--) { /* convert item to C string */ if (!XmStringGetLtoR (strlist[cnt], XmFONTLIST_DEFAULT_TAG, &text)) break; /* do pattern match against search string */ #ifdef SYSV /* returns NULL if match failed */ matched = regex (exp, text, NULL) != NULL; #else /* BSD */ /* -1 on error, 0 if no-match, 1 if match */ matched = re_exec (text) > 0; #endif /* SYSV */ if (matched) { selectlist = (XmString *) XtRealloc (selectlist, (j+1) * (sizeof (XmString *))); selectlist[j++] = XmStringCopy (strlist[cnt]); } XtFree (text); } #ifdef SYSV free (exp); /* this must be freed for regcmp() */ #endif /* SYSV */ XtFree (newtext); /* set the actual selected items to be those that matched */ XtVaSetValues (list_w, XmNselectedItems, selectlist, XmNselectedItemCount, j, NULL); while (j--) XmStringFree (selectlist[j]); XmTextFieldSetString (text_w, ""); } The output of this program is shown in the figure. The TextField widget that is used to search for items in the List widget works identically to the one that is used to add new items. Its callback routine,search_item(), searches the list for the specified pattern. The version of UNIX you are running (System V or BSD) dictates which kind of regular expression matching is done. System V machines use the functionregcmp() to compile the pattern andregex() to search for the pattern within another string, while BSD UNIX systems use the functionsre_comp() andre_exec() to do the same thing. Systems that support both BSD and System V may support one, the other, or both methods of regular expression handling. You should consult your system's documentation for more information on these functions.The items in the list are retrieved usingXtVaGetValues() and thestrlist parameter. This variable points to the internal list used by the List widget, so it is important that we do not change any of these elements or free these pointers when we are through with them. Changing the value ofXmNselectedItems causes the internal list to change. Since the internal list is referenced bystrlist, it is important to copy any values that we want to use elsewhere. If the pattern matches a list item, the item is copied usingXmStringCopy() and is later added to the List'sXmNselectedItems.

The items within a List can be positioned such that an arbitrary element is placed at the top or bottom of the List. If the List is being used as part of a ScrolledList, the item is placed at the top or bottom of the viewport of the ScrolledWindow. To position a particular item at the top or bottom of the window, use eitherXmListSetItem() orXmListSetBottomItem(). These routines take the following form:
void XmListSetItem(list_w,item) Widgetlist_w; XmStringitem;
void XmListBottomItem(list_w,item) Widgetlist_w; XmStringitem;
Both of these functions require anXmString parameter to reference a particular item in the list. However, if you know the position of the item, you can useXmListSetPos() orXmListSetBottomPos() instead. These functions take the following form:
void XmListSetPos(list_w,position) Widgetlist_w; intposition;
void XmListSetBottomPos(list_w,position) Widgetlist_w; intposition;Theposition parameter can be set to0 to specify that the last item be positioned at the bottom of the viewport. Through a mixture of resource values and simple calculations, you can position any particular item anywhere in the list. For example, if you have an item that you want to be sure is visible, but you are not concerned about where in the viewport it is displayed, you can write a function to make the item visible. the source code shows theMakePosVisible() routine, which makes sure that the item at a specified position is visible.
void MakePosVisible(list_w, item_no) Widget list_w; int item_no; { int top, visible; XtVaGetValues (list_w, XmNtopItemPosition, &top, XmNvisibleItemCount, &visible, NULL); if (item_no < top) XmListSetPos (list_w, item_no); else if (item_no >= top + visible) XmListSetBottomPos (list_w, item_no); } The function gets the number of visible items and the position of the item at the top of the viewport. TheXmNtopItemPosition resource stores this information. If the item comes beforetop,item_no is set to the top of the List usingXmListSetPos(). If it comes aftertop + visible, the item is set at the bottom of the List usingXmListSetBottomPos(). If you don't know the position of the item in the List, you can write a function that makes a specified item visible, as shown in the source code MakeItemVisible(list_w, item) Widget list_w; XmString item; { int item_no = XmListItemPos (list_w, item); if (item_no > 0) MakePosVisible (list_w, item_no); } TheMakeItemVisible() routine simple gets the position of the given item in the list usingXmListItemPos() and callsMakePosVisible(). In Motif 1.2, there are some new routines that deal with positions in a List. TheXmListGetKbdItemPos() andXmListSetKbdItemPos() routines retrieve and set the item in the List that has the location cursor. These routines take the following form:
int XmListGetKbdItemPos(list_w) Widgetlist_w;
Boolean XmListSetKbdItemPos(list_w,position) Widgetlist_w; intposition;XmListGetKbdItemPos() returns the position of the item that has the location cursor, whileXmListSetKbdItemPos() provides a way to specify the position of this item.
TheXmListPosToBounds() andXmListYToPos() functions in Motif 1.2 provide a way to translate list items to x,y coordinates and vice versa.XmListPosToBounds() returns the bounding box of the item at a specified position in a List. This routine takes the following form:
Boolean XmListPosToBounds(list_w,position,x,y,width,height) Widgetlist_w; intposition; Position *x; Position *y; Dimension *width; Dimension *height;This routine returnsTrue if the item at the specified position is visible andFalse otherwise. If the item is visible, the return parameters specify the bounding box of the item. This information can be useful if you need to perform additional event processing or draw special graphics for the item. TheXmListYToPos() routine returns the position of the List item at a specified y-coordinate. This function takes the following form:
int XmListYToPos(list_w,y) Widgetlist_w; Positiony;The position information returned by this routine can be useful if you are processing events that report a pointer position and you need to convert the location of the event into an item position.
While the callback routines associated with the List widget are not affected by whether the List is scrollable, they do depend on the selection policy currently in use. There is a separate callback resource for each selection policy, plus a callback for the default action. The default action is invoked when the left mouse button is double-clicked on an item or the RETURN key is pressed. The callback resources are:
XmNbrowseSelectionCallback XmNdefaultActionCallback XmNextendedSelectionCallback XmNmultipleSelectionCallback XmNsingleSelectionCallback
In all of the selection modes there is the concept of the default action. This term refers to the action that is taken when the user double clicks the left mouse button on an item or presses the RETURN key when an item has the location cursor. The default action always indicates that the active item should be selected, regardless of the selection policy. TheXmNdefaultActionCallback is invoked for the default action.
The default selection is activated when the user double clicks on a List item. The time interval between two consecutive button clicks determines whether the clicks are interpreted as individual clicks or as a double click. You can set or get the time interval using theXmNdoubleClickInterval resource. The value is stored as milliseconds, so a value of500 is half a second. If the resource is not set, the value of themultiClickTime resource is used instead. This resource is a fundamental X resource that is understood by all X applications; it is not an Xt or Motif toolkit resource. You should let the user specify the double-click interval in a resource file; the value should be set using the more globalmultiClickTime resource.
The browse and single selection modes only allow the selection of a single item. The browsing mode is regarded as a simpler interface for the user. Interactively, browse selection allows the user to drag the selection over many items; the selection is not made till the mouse button is released. In the single selection mode, the selection is made as soon as the mouse button is pressed. For browse selection, the callback list associated with theXmNbrowseSelectionCallback is used, while theXmNsingleSelectionCallback is used for the single selection mode.
Keyboard traversal in the List is also different between the two modes. If the user uses the keyboard to move from one item to the next in single selection mode, theXmNsingleSelectCallback is not invoked until the SPACEBAR is pressed. In browse selection, theXmNbrowseSelectionCallback is invoked for each item the user traverses. Since these two modes for the List widget are visually similar, your treatment of the callbacks is very important for maintaining consistency between Lists that use different selection modes.
A simple example of using callbacks with a List widget is shown in the source codeXtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4.XmStringCreateLocalized() is only available in Motif 1.2;XmStringCreateSimple() is the corresponding function in Motif 1.1.XmFONTLIST_DEFAULT_TAG replacesXmSTRING_DEFAULT_CHARSET in Motif 1.2.
/* browse.c -- specify a browse selection callback for a simple List. */ #include <Xm/List.h> char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; main(argc, argv) int argc; char *argv[]; { Widget toplevel, list_w; XtAppContext app; int i, n = XtNumber (months); XmStringTable str_list; void sel_callback(); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); str_list = (XmStringTable) XtMalloc (n * sizeof (XmString *)); for (i = 0; i < n; i++) str_list[i] = XmStringCreateLocalized (months[i]); list_w = XmCreateScrolledList (toplevel, "months", NULL, 0); XtVaSetValues (list_w, XmNvisibleItemCount, n, XmNitemCount, n, XmNitems, str_list, NULL); XtManageChild (list_w); XtAddCallback (list_w, XmNdefaultActionCallback, sel_callback, NULL); XtAddCallback (list_w, XmNbrowseSelectionCallback, sel_callback, NULL); for (i = 0; i < n; i++) XmStringFree (str_list[i]); XtFree (str_list); XtRealizeWidget (toplevel); XtAppMainLoop (app); } void sel_callback(list_w, client_data, call_data) Widget list_w; XtPointer client_data; XtPointer call_data; { XmListCallbackStruct *cbs = (XmListCallbackStruct *) call_data; char *choice; if (cbs->reason == XmCR_BROWSE_SELECT) printf ("Browse selection -- "); else printf ("Default action -- "); XmStringGetLtoR (cbs->item, XmFONTLIST_DEFAULT_TAG, &choice); printf ("selected item: %s (%d)0, choice, cbs->item_position); XtFree (choice); } For this example, we modified our previous example that uses a ScrolledList to display the months of the year. We have added the same callback routine,sel_callback(), to theXmNbrowseSelectionCallback andXmNdefaultActionCallback resources. Since the default action may happen for any List widget, it is advisable to set this callback, even if there are other callbacks. The callback routine prints the type of action performed by the user and the selection that was made. The callback structure is used to get information about the nature of the List widget and the selection made. The List callbacks provide a callback structure of typeXmListCallbackStruct, which is defined as follows:
typedef struct { int reason; XEvent *event; XmString item; int item_length; int item_position; XmString *selected_items; int selected_item_count; int *selected_item_positions; char selection_type; } XmListCallbackStruct; Thereason field specifies the reason that the callback was invoked, which corresponds to the type of action performed by the user. The possible values for this field are:XmCR_BROWSE_SELECT XmCR_DEFAULT_ACTION XmCR_EXTENDED_SELECT XmCR_MULTIPLE_SELECT XmCR_SINGLE_SELECTThereason field is important with List callbacks because not all of the fields in the callback structure are valid for every reason. For the browse and single selection policies, thereason,event,item,item_length, anditem_position fields are valid. For the default action, all of the fields are valid. List items are stored as compound strings in the callback structure, so to print an item usingprintf(), we must convert the string with the compound string functionXmStringGetLtoR().
WhenXmNselectionPolicy is set toXmMULTIPLE_SELECT, multiple items can be selected in the List widget. When the user selects an item, its selection state is toggled. Each time the user selects an item, the callback routine associated with theXmNmultipleSelectionCallback is invoked. the source code shows thesel_callback() routine that could be used with a multiple selection List.XmFONTLIST_DEFAULT_TAG replacesXmSTRING_DEFAULT_CHARSET in Motif 1.2.
void sel_callback(list_w, client_data, call_data) Widget list_w; XtPointer client_data; XtPointer call_data; { XmListCallbackStruct *cbs = (XmListCallbackStruct *) call_data; char *choice; int i; if (cbs->reason == XmCR_MULTIPLE_SELECT) { printf ("Multiple selection -- %d items selected:0, cbs->selected_item_count); for (i = 0; i < cbs->selected_item_count; i++) { XmStringGetLtoR (cbs->selected_items[i], XmFONTLIST_DEFAULT_TAG, &choice); printf ("%s (%d)0, choice, cbs->selected_item_positions[i]); XtFree (choice); } } else { XmStringGetLtoR (cbs->item, XmFONTLIST_DEFAULT_TAG, &choice); printf ("Default action -- selected item %s (%d)0, choice, cbs->item_position); XtFree (choice); } } The routine tests the callback structure'sreason field to determine whether the callback was invoked as a result of a multiple selection action or the default action. When thereason isXmCR_MULTIPLE_SELECT, we print the list of selected items by looping throughselected_items andselected_item_positions. With this reason, all of the fields in the callback structure exceptselection_type are valid. If thereason isXmCR_DEFAULT_ACTION, there is only one item selected, since the default selection action causes all of the other items to be deselected. With the extended selection model, the user has the greatest flexibility to select and deselect individual items or ranges of items. TheXmNextendedSelectionCallback is invoked whenever the user makes a selection or modifies the selection. the source code demonstrates thesel_callback() routine that could be used with an extended selection List.XmFONTLIST_DEFAULT_TAG replacesXmSTRING_DEFAULT_CHARSET in Motif 1.2.
void sel_callback(list_w, client_data, call_data) Widget list_w; XtPointer client_data; XtPointer call_data; { XmListCallbackStruct *cbs = (XmListCallbackStruct *) call_data; char *choice; int i; if (cbs->reason == XmCR_EXTENDED_SELECT) { if (cbs->selection_type == XmINITIAL) printf ("Extended selection -- initial selection: "); else if (cbs->selection_type == XmMODIFICATION) printf ("Extended selction -- modification of selection: "); else /* selection type = XmADDITION */ printf ("Extended selection -- additional selection: "); printf ("%d items selected0, cbs->selected_item_count); for (i = 0; i < cbs->selected_item_count; i++) { XmStringGetLtoR (cbs->selected_items[i], XmFONTLIST_DEFAULT_TAG, &choice); printf ("%s (%d)0, choice, cbs->selected_item_positions[i]); XtFree (choice); } } else { XmStringGetLtoR (cbs->item, XmFONTLIST_DEFAULT_TAG, &choice); printf ("Default action -- selected item %s (%d)0, choice, cbs->item_position); XtFree (choice); } } Most of the callback routine is the same as it was for multiple selection mode. With an extended selection callback, theselection_type field is also valid. This field can have the following values:XmINITIAL XmMODIFICATION XmADDITIONTheXmINITIAL value indicates that the selection is an initial selection for the List. All previously-selected items are deselected and the items selected with this action comprise the entire list of selected items. The value isXmMODIFICATION when the user modifies the selected list by using the SHIFT key in combination with a selection action. In this case, the selected item list contains some items that were already selected before this action took place.XmADDITION indicates that the items that are selected are in addition to what was previously selected. The user can select additional items by using the CTRL key in combination with a selection action. Regardless of the value forselection_type, theselected_items andselected_item_positions fields always reflect the set of currently selected items.
The List widget is a powerful user interface tool that has a simple design. The programming interface to the widget is mostly mechanical. The List allows you to present a vast list of choices to the user, although the choices themselves must be textual in nature. Lists are not suitable for all situations however, as they cannot display choices other than text (pixmaps cannot be used as selection items) and there is no ability to set color on individual items. Even with these shortcomings, the List widget is still a visible and intuitive object that can be used in designing a graphical user interface.
The following exercises expand on some of the concepts presented in this chapter.