.\" Copyright (c) 2002-2023 Julien Nadeau Carriere .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, .\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES .\" (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR .\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, .\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd February 10, 2023 .Dt AG_TLIST 3 .Os Agar 1.7 .Sh NAME .Nm AG_Tlist .Nd agar tree/list widget .Sh SYNOPSIS .Bd -literal #include #include .Ed .Sh DESCRIPTION .\" IMAGE(/widgets/AG_Tlist.png, "An AG_Tlist displaying a tree") The .Nm widget shows a scrollable tree / list of clickable and selectable items. Items can display text and icons. Items include user-defined data fields (a signed integer .Va v , an unsigned integer .Va u , a user pointer .Va p1 and a general-purpose category string .Va cat ) . .Pp For cases where the contents of the tree or list may change externally, a .Em polling routine may be defined. Such a routine is expected to clear and repopulate the list entirely. It may be run periodically at a specified interval, whenever user interaction events occur, or by explicit call to .Fn AG_TlistRefresh . To make polling more efficient (and to preserve per-item states such as selections across polling cycles), .Nm relies on a .Em compare function to test items for equivalence and reuse assets (including allocated structures and rendered text surfaces). .Sh INHERITANCE HIERARCHY .Xr AG_Object 3 -> .Xr AG_Widget 3 -> .Nm . .Sh INITIALIZATION .nr nS 1 .Ft "AG_Tlist *" .Fn AG_TlistNew "AG_Widget *parent" "Uint flags" .Pp .Ft "AG_Tlist *" .Fn AG_TlistNewPolled "AG_Widget *parent" "Uint flags" "void (*eventFn)(AG_Event *)" "const char *eventArgs" "..." .Pp .Ft "AG_Tlist *" .Fn AG_TlistNewPolledMs "AG_Widget *parent" "Uint flags" "int ms" "void (*eventFn)(AG_Event *)" "const char *eventArgs" "..." .Pp .Ft void .Fn AG_TlistSetRefresh "AG_Tlist *tl" "int ms" .Pp .Ft void .Fn AG_TlistRefresh "AG_Tlist *tl" .Pp .Ft void .Fn AG_TlistSetItemHeight "AG_Tlist *tl" "int item_height" .Pp .Ft void .Fn AG_TlistSetIconWidth "AG_Tlist *tl" "int icon_width" .Pp .Ft void .Fn AG_TlistSizeHint "AG_Tlist *tl" "const char *text" "int nItems" .Pp .Ft void .Fn AG_TlistSizeHintPixels "AG_Tlist *tl" "int w" "int nItems" .Pp .Ft void .Fn AG_TlistSizeHintLargest "AG_Tlist *tl" "int nItems" .Pp .Ft void .Fn AG_TlistSetDblClickFn "AG_Tlist *tl" "AG_EventFn fn" "const char *fn_args" "..." .Pp .Ft void .Fn AG_TlistSetChangedFn "AG_Tlist *tl" "AG_EventFn fn" "const char *fn_args" "..." .Pp .nr nS 0 .Fn AG_TlistNew allocates, initializes, and attaches a .Nm widget. .Pp The .Fn AG_TlistNewPolled variant enables .Dv AG_TLIST_POLL and sets the specified polling routine .Fa eventFn to run periodically (or -1 = never). The polling routine is expected to clear and populate the .Nm . The .Fn AG_TlistNewPolledMs variant sets the update rate to .Fa ms milliseconds. .Pp Acceptable .Fa flags include: .Bl -tag -width "AG_TLIST_FIXED_HEIGHT " .It AG_TLIST_EXPAND_NODES Expand nodes with child items by default. .It AG_TLIST_MULTI Allow multiple selections (with ctrl/shift). .It AG_TLIST_MULTITOGGLE Enforce multiple selections (without ctrl/shift). .It AG_TLIST_POLL List contents are updated dynamically by the "tlist-poll" handler. This handler is expected to wrap its .Fn AG_TlistAdd calls between .Fn AG_TlistBegin and .Fn AG_TlistEnd . Implied by .Fn AG_TlistNewPolled . .It AG_TLIST_FIXED_HEIGHT Don't reset the item height on "font-changed". .It AG_TLIST_NO_LINES Don't draw lines between nodes. .It AG_TLIST_NO_KEYREPEAT Disable keyrepeat behavior. .It AG_TLIST_NO_SELECTED Don't raise "tlist-selected" on select. .It AG_TLIST_STATELESS When using .Dv AG_TLIST_POLL , don't preserve selection information across list updates. .It AG_TLIST_HFILL Expand horizontally in parent container. .It AG_TLIST_VFILL Expand vertically in parent container. .It AG_TLIST_EXPAND Shorthand for .Dv AG_TLIST_HFILL | AG_TLIST_VFILL . .El .Pp .Fn AG_TlistSetRefresh sets the rate at which the polling routine will be invoked in milliseconds (default = 1s). A value of -1 disables automatic updates. When automatic updates are disabled, updates can be performed by calling .Fn AG_TlistRefresh . See the .Dv AG_TLIST_POLL option for details. .Pp .Fn AG_TlistSetItemHeight sets the given, uniform height of all items. .Fn AG_TlistSetIconWidth sets the width of item icons in pixels. .Pp .Fn AG_TlistSizeHint requests sufficient height to display .Fa nItems items at once, and sufficient width to display an item containing .Fa text . The .Fn AG_TlistSizeHintPixels variant accepts a width argument in pixels instead of a string and the .Fn AG_TlistSizeHintLargest variant uses the largest text label in the current list of items to determine the width. .Pp .Fn AG_TlistSetDblClickFn arranges for the given callback .Fa fn to be invoked with the given arguments (as well as the item pointer) when the user double clicks on an item. .Pp .Fn AG_TlistSetChangedFn arranges for the callback .Fa fn to be invoked when the user selects or deselects an item. The arguments are respectively, a pointer to the item and an integer with a value of 0 to indicate deselection and 1 to indicate selection. .\" MANLINK(AG_TlistCompareFn) .Sh COMPARE FUNCTIONS .nr nS 1 .Ft AG_TlistCompareFn .Fn AG_TlistSetCompareFn "AG_Tlist *tl" "AG_TlistCompareFn fn" .Pp .Ft int .Fn AG_TlistCompareInts "const AG_TlistItem *a, const AG_TlistItem *b) .Pp .Ft int .Fn AG_TlistCompareUints "const AG_TlistItem *a, const AG_TlistItem *b) .Pp .Ft int .Fn AG_TlistCompareStrings "const AG_TlistItem *a, const AG_TlistItem *b) .Pp .Ft int .Fn AG_TlistComparePtrs "const AG_TlistItem *a, const AG_TlistItem *b) .Pp .Ft int .Fn AG_TlistComparePtrsAndCats "const AG_TlistItem *a, const AG_TlistItem *b) .Pp In order for .Nm to be able to preserve per-item states (such as selections) through polling updates when using a polling function, it is necessary to be able to establish equivalence between items. The nature of this equivalence is application-defined and is determined by the .Em compare function selected. The .Fn AG_TlistUniq routine (see below) also relies on the compare function. .Pp .Fn AG_TlistSetCompareFn sets the comparison function to use when testing between two .Ft AG_TlistItem for equivalence. It returns a pointer to the previously selected compare function. Compare functions are defined as: .Bd -literal .\" SYNTAX(c) typedef int (*AG_TlistCompareFn)(const AG_TlistItem *a, const AG_TlistItem *b); .Ed .Pp It is expected to return 0 if the two items are equivalent and non-zero if the items are not. If the returned value if non-zero, it can be used to define the sorting order. Returning a positive value in the case where (a > b) should produce a sort in descending order. .Pp Some basic compare functions are provided: .Pp .Fn AG_TlistCompareInts compares the signed integer field .Va v1 . .Pp .Fn AG_TlistCompareUints compares the unsigned integer field .Va u . .Pp .Fn AG_TlistCompareStrings compares the .Va text strings lexicographically. The comparison is case-sensitive and ignores any locale collation. .Pp .Fn AG_TlistComparePtrs compares the pointer fields .Va p1 . This is the default compare function. .Pp .Fn AG_TlistComparePtrsAndCats compares both .Va p1 and the category .Va cat . .\" MANLINK(AG_TlistItem) .Sh MANIPULATING ITEMS .nr nS 1 .Ft "AG_TlistItem *" .Fn AG_TlistAdd "AG_Tlist *tl" "const AG_Surface *icon" "const char *format" "..." .Pp .Ft "AG_TlistItem *" .Fn AG_TlistAddS "AG_Tlist *tl" "const AG_Surface *icon" "const char *text" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistAddHead "AG_Tlist *tl" "const AG_Surface *icon" "const char *format" "..." .Pp .Ft "AG_TlistItem *" .Fn AG_TlistAddHeadS "AG_Tlist *tl" "const AG_Surface *icon" "const char *text" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistAddPtr "AG_Tlist *tl" "const AG_Surface *icon" "const char *text" "const void *p1" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistAddPtrHead "AG_Tlist *tl" "const AG_Surface *icon" "const char *text" "const void *p1" .Pp .Ft void .Fn AG_TlistMoveToHead "AG_Tlist *tl" "AG_TlistItem *item" .Pp .Ft void .Fn AG_TlistMoveToTail "AG_Tlist *tl" "AG_TlistItem *item" .Pp .Ft void .Fn AG_TlistCopy "AG_Tlist *tlDst" "const AG_Tlist *tlSrc" .Pp .Ft void .Fn AG_TlistSetIcon "AG_Tlist *tl" "AG_TlistItem *item" "const AG_Surface *icon" .Pp .Ft void .Fn AG_TlistSetColor "AG_Tlist *tl" "AG_TlistItem *item" "const AG_Color *color" .Pp .Ft void .Fn AG_TlistSetFont "AG_Tlist *tl" "AG_TlistItem *item" "const char *face" "float scale" "Uint flags" .Pp .Ft "void" .Fn AG_TlistDel "AG_Tlist *tl" "AG_TlistItem *item" .Pp .Ft "void" .Fn AG_TlistSort "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistSortByInt "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistUniq "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistClear "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistBegin "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistEnd "AG_Tlist *tl" .Pp .Ft "int" .Fn AG_TlistVisibleChildren "AG_Tlist *tl" "AG_TlistItem *item" .Pp .Ft "void" .Fn AG_TlistSelect "AG_Tlist *tl" "AG_TlistItem *item" .Pp .Ft "void" .Fn AG_TlistSelectIdx "AG_Tlist *tl" "Uint index" .Pp .Ft "void" .Fn AG_TlistSelectAll "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistDeselect "AG_Tlist *tl" "AG_TlistItem *item" .Pp .Ft "void" .Fn AG_TlistDeselectIdx "AG_Tlist *tl" "Uint index" .Pp .Ft "void" .Fn AG_TlistDeselectAll "AG_Tlist *tl" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistSelectPtr "AG_Tlist *tl" "void *ptr" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistSelectText "AG_Tlist *tl" "const char *text" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistFindByIndex "AG_Tlist *tl" "int index" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistSelectedItem "AG_Tlist *tl" .Pp .Ft "void *" .Fn AG_TlistSelectedItemPtr "AG_Tlist *tl" .Pp .Ft "void *" .Fn AG_TLIST_ITEM "idx" .Pp .Ft "int" .Fn AG_TlistFindPtr "AG_Tlist *tl" "void **p" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistFindText "AG_Tlist *tl" "const char *text" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistFirstItem "AG_Tlist *tl" .Pp .Ft "AG_TlistItem *" .Fn AG_TlistLastItem "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistScrollToStart "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistScrollToEnd "AG_Tlist *tl" .Pp .Ft "void" .Fn AG_TlistScrollToSelection "AG_Tlist *tl" .Pp .nr nS 0 .Fn AG_TlistAdd inserts a newly-allocated item into the list and returns a pointer to it. If .Fa icon is not NULL, it specifies an .Xr AG_Surface 3 to duplicate and display as an icon. .Pp The .Fn AG_TlistAddHead variant places the item at the head of the list. The .Fn AG_TlistAddPtr variant of .Fn AG_TlistAdd also sets the pointer field .Fa p1 on initialization. .Pp .Fn AG_TlistAddPtrHead places the item at the head of the list. .Pp .Fn AG_TlistMoveToHead moves an existing item .Fa item to the head of the list. .Fn AG_TlistMoveToTail moves an existing item .Fa item to the tail of the list. .Pp The .Fn AG_TlistCopy routine copies all items from .Fa tlSrc into .Fa tlDst . Item attributes and selection flags are preserved. .Pp .Fn AG_TlistSetIcon sets the icon surface associated with .Fa item . A duplicate of the given surface will be used. .Pp .Fn AG_TlistSetColor sets an alternate text color for the specified item (or NULL to switch back to the default). .Pp .Fn AG_TlistSetFont sets an alternate font for the specified item. If .Fa face is NULL, use the same font face as the tlist's font. .Fa scale sets the size of the font relative to the tlist's font. If .Fa scale is 1.0f then the same size will be used. The .Fa flags argument is the set of .Xr AG_Font 3 style flags (such as .Dv AG_FONT_BOLD ) . .Pp The .Fn AG_TlistDel function detaches and frees .Fa item from its parent .Nm tl . .Pp The .Fn AG_TlistSort routine lexicographically sorts the items in the list by text according to the current locale collation. .Pp .Fn AG_TlistSortByInt sorts items based on their integer values .Va v . .Pp .Fn AG_TlistUniq scans the list for duplicates and removes them. .Fn AG_TlistUniq uses the compare function to determine equivalence between items (see .Fn AG_TlistSetCompareFn ) . .Pp .Fn AG_TlistClear removes all items attached to the list. .Pp The .Fn AG_TlistBegin function removes all items attached to .Fa tl , but remembers their selection and child item expansion states. .Fn AG_TlistEnd compares each item against the saved state and restores the selection and child item expansion states accordingly. .Pp The .Fn AG_TlistVisibleChildren function is meant to be called from a polling routine. It tests whether a newly-created .Fa item should make its own child items visible based on the previously saved state. If there are no matching items in the saved state (according to the Compare function), then it returns the default visibility setting (which is 1 if the .Dv AG_TLIST_EXPAND_NODES option is set, otherwise 0). .Pp .Fn AG_TlistSelect sets the selection flag on .Fa item (clearing any previous selection unless .Dv AG_TLIST_MULTI is set). .Fn AG_TlistDeselect clears the selection flag on .Fa item . .Fn AG_TlistSelectIdx and .Fn AG_TlistDeselectIdx reference the target .Ft AG_TlistItem by index rather than by pointer. .Pp .Fn AG_TlistSelectAll .Fn AG_TlistDeselectAll sets / clears the selection on all items attached to .Fa tl . .Pp The .Fn AG_TlistSelectPtr function selects and returns the first item with a user pointer value matching .Fa ptr . Similarly, .Fn AG_TlistSelectText selects and returns the first item with a text field equal to .Fa text . Both of these functions invoke "tlist-poll" if the .Dv AG_TLIST_POLL option is set. .Pp The .Fn AG_TlistFindByIndex function returns the item at .Fa index , or NULL if there is no such item. The .Fn AG_TlistSelectedItem function returns the first selected item, or NULL if there are none. .Pp The .Fn AG_TlistSelectedItemPtr function returns the user pointer of the first selected item, or NULL if there is no selected item. It is not possible to distinguish a non-existent selection from an actual selection with a NULL user pointer using this function. .Pp In event handler context, the .Fn AG_TLIST_ITEM macro is a shortcut for .Fn AG_TlistSelectedItemPtr on item .Fa n from the event stack. .Pp The .Fn AG_TlistFindPtr variant copies the user pointer associated with the first selected item into .Fa p , returning 0 on success or -1 if there is no item selected. The .Fn AG_TlistFindText function searches .Fa tl for an item containing the .Fa text string and returns NULL if there is no such item. .Pp The .Fn AG_TlistFirstItem and .Fn AG_TlistLastItem functions return the first and last items on the list. .Pp .Fn AG_TlistScrollToStart scrolls the display to the start and .Fn AG_TlistScrollToEnd scrolls the display to the end of the list. .Pp .Fn AG_TlistScrollToSelection scrolls to make the first selected item visible. It's a no-op if there are no selected items. .Sh POPUP MENUS .nr nS 1 .Ft "AG_MenuItem *" .Fn AG_TlistSetPopupFn "AG_Tlist *tl" "AG_EventFn fn" "const char *fn_args" "..." .Pp .Ft "AG_MenuItem *" .Fn AG_TlistSetPopup "AG_Tlist *tl" "const char *category" .Pp .nr nS 0 The .Fn AG_TlistSetPopupFn function arranges for the given callback .Fa fn to be invoked with the given arguments whenever the user right-clicks on an item on the list. A pointer to the selected item is passed as the last argument to this function. Typically, the function will use .Xr AG_PopupNew 3 to display a popup menu. .Pp The .Fn AG_TlistSetPopup function creates a popup menu that will be displayed when the user right-clicks on any item that matches the given category string. .Sh EVENTS The .Nm widget generates the following events: .Pp .Bl -tag -compact -width 2n .It Fn tlist-changed "AG_TlistItem *item" "int state" .Fa item was selected or unselected. .It Fn tlist-selected "AG_TlistItem *item" .Fa item was selected. .It Fn tlist-dblclick "AG_TlistItem *item" The user just double-clicked .Fa item . Binding to this event is equivalent to using .Fn AG_TlistSetDblClickFn . .It Fn tlist-return "AG_TlistItem *item" The user has selected .Fa item and pressed the return key. .It Fn tlist-poll "void" The .Dv AG_TLIST_POLL flag is set and the widget is about to be drawn or an event is being processed. .El .Sh BINDINGS The .Nm widget provides the following bindings: .Pp .Bl -tag -compact -width "void *selected " .It Ft "void *selected" The .Va p1 (user pointer) value of the selected item, or NULL if there is no selection. The value of this binding is undefined if the .Dv AG_TLIST_MULTI or .Dv AG_TLIST_MULTITOGGLE flags are in use. .El .Sh STRUCTURE DATA For the .Ft AG_Tlist object: .Pp .Bl -tag -compact -width "Uint pollDelay " .It Ft TAILQ items The list of items (linkage is read-only). .It Ft int nItems Number of items in total (read-only). .It Ft int nVisible Number of items on screen (read-only). .It Ft Uint pollDelay Delay in between updates in .Dv AG_TLIST_POLL mode (ms). .El .Pp For the .Ft AG_TlistItem structure: .Pp .Bl -tag -compact -width "const char *cat " .It Ft int selected Selection flag. .It Ft void *p1 User pointer (application-defined). .It Ft int v User signed integer value (application-defined). .It Ft Uint u User unsigned integer value (application-defined). .It Ft const char *cat Category string (application-defined). .It Ft char text[] Text label (up to .Dv AG_TLIST_LABEL_MAX characters). .It Ft int depth Depth in tree (0 = root). .It Ft Uint flags Item flags (see .Sx ITEM FLAGS section below). .It Ft float scale Scaling factor for text display (default = 1.0). .El .Sh ITEM FLAGS .Bl -tag -compact -width "AG_TLIST_ITEM_UPPERCASE " .It AG_TLIST_ITEM_DISABLED Disable selection and draw in DISABLED style. .It AG_TLIST_NO_SELECT Disable selection and draw in DEFAULT style. .It AG_TLIST_ITEM_EXPANDED Child items are visible. .It AG_TLIST_HAS_CHILDREN At least one child item exists. .It AG_TLIST_NO_POPUP Disable popup menus (if any have been created). .El .Sh EXAMPLES The following code fragment displays an external tree structure using a recursive polling routine. .Bd -literal -offset indent .\" SYNTAX(c) MyTreeNode *myTreeRoot; AG_Window *win; static void PollMyTreeNode(AG_Tlist *tl, MyTreeNode *node, int depth) { AG_TlistItem *ti; ti = AG_TlistAdd(tl, NULL, "Node %s", node->name); ti->flags |= AG_TLIST_HAS_CHILDREN; ti->p1 = node; ti->depth = depth; if (AG_TlistVisibleChildren(tl, ti)) { MyTreeNode *child; LIST_FOREACH(child, &node->children, children) PollMyTreeNode(tl, child, depth+1); } } static void PollMyTree(AG_Event *event) { AG_Tlist *tl = AG_TLIST_SELF(); MyTreeNode *root = AG_PTR(1); AG_TlistBegin(tl); PollMyTreeNode(tl, root, 0); AG_TlistEnd(tl); } myTreeRoot = InitMyTree(); win = AG_WindowNew(0); AG_TlistNewPolled(win, 0, PollMyTree, "%p", myTreeRoot); .Ed .Sh SEE ALSO .Xr AG_Intro 3 , .Xr AG_Table 3 , .Xr AG_Treetbl 3 , .Xr AG_Widget 3 , .Xr AG_Window 3 .Sh HISTORY The .Nm widget first appeared in Agar 1.0. .Fn AG_TlistSelectIdx , .Fn AG_TlistDeselectIdx , .Fn AG_TlistSetColor and .Fn AG_TlistSetFont appeared in Agar 1.6.0. Options .Dv AG_TLIST_EXPAND_NODES , .Dv AG_TLIST_FIXED_HEIGHT , .Dv AG_TLIST_NO_LINES , .Dv AG_TLIST_NO_KEYREPEAT and the per-item flag .Dv AG_TLIST_ITEM_DISABLED appeared in Agar 1.7.0. The .Va u and .Va v integer fields and .Fn AG_TlistCompareInts and .Fn AG_TlistCompareUints appeared in Agar 1.7.0.