diff --git a/doc/man/index.html b/doc/man/index.html
index 2429a95c17..c91c99753d 100644
--- a/doc/man/index.html
+++ b/doc/man/index.html
@@ -66,7 +66,6 @@
C library (section 3)
notcurses_stats—notcurses runtime statistics
notcurses_stdplane—acquire the standard ncplane
notcurses_stop—collapse the context
- notcurses_tree—high-level hierarchical line-based data
notcurses_visual—operations on ncvisual objects
diff --git a/doc/man/man3/notcurses.3.md b/doc/man/man3/notcurses.3.md
index e5ba60d34c..e1a6d23c6f 100644
--- a/doc/man/man3/notcurses.3.md
+++ b/doc/man/man3/notcurses.3.md
@@ -118,7 +118,6 @@ A few high-level widgets are included, all built atop ncplanes:
* **notcurses_reader(3)** for free-form input data
* **notcurses_reel(3)** for hierarchal display of block-based data
* **notcurses_selector(3)** for selecting one item from a set
-* **notcurses_tree(3)** for hierarchal display of line-based data
## Threads
@@ -187,7 +186,6 @@ order to turn most error returns into exceptions.
**notcurses_stats(3)**,
**notcurses_stdplane(3)**,
**notcurses_stop(3)**,
-**notcurses_tree(3)**,
**notcurses_visual(3)**,
**terminfo(5)**, **ascii(7)**, **utf-8(7)**,
**unicode(7)**
diff --git a/doc/man/man3/notcurses_reel.3.md b/doc/man/man3/notcurses_reel.3.md
index 753dfcd143..837f846ce0 100644
--- a/doc/man/man3/notcurses_reel.3.md
+++ b/doc/man/man3/notcurses_reel.3.md
@@ -127,12 +127,6 @@ tablet, unless no tablets exist, in which case they return **NULL**.
**ncreel_add** returns the newly-added **nctablet**.
-# NOTES
-
-**ncreel** shares many properties with **notcurses_tree**. Unlike the latter,
-**ncreel**s supply only a single level of hierarchy, and allow elements to
-come and go across the lifetime of the widget.
-
# BUGS
I can't decide whether to require the user to explicitly call **ncreel_redraw**.
@@ -143,5 +137,4 @@ things more complicated for both me and the user.
**notcurses(3)**,
**notcurses_input(3)**,
-**notcurses_plane(3)**,
-**notcurses_tree(3)**
+**notcurses_plane(3)**
diff --git a/doc/man/man3/notcurses_tree.3.md b/doc/man/man3/notcurses_tree.3.md
deleted file mode 100644
index 93b37f225c..0000000000
--- a/doc/man/man3/notcurses_tree.3.md
+++ /dev/null
@@ -1,94 +0,0 @@
-% notcurses_tree(3)
-% nick black
-% v2.2.1
-
-# NAME
-
-notcurses_tree - high-level hierarchical line-based data
-
-# SYNOPSIS
-
-**#include **
-
-```c
-struct nctree;
-struct ncplane;
-
-typedef struct nctree_item {
- void* curry;
- struct nctree_item* subs;
- unsigned subcount;
-} nctree_item;
-
-typedef struct nctree_options {
- const nctree_item* items; // top-level nctree_item array
- unsigned count; // size of |items|
- uint64_t bchannels; // base channels
- int (*nctreecb)(struct ncplane*, void*, int); // item callback
- uint64_t flags; // bitfield of NCTREE_OPTION_*
-} nctree_options;
-```
-
-**struct nctree* nctree_create(struct ncplane* ***n***, const nctree_options* ***opts***);**
-
-**struct ncplane* nctree_plane(struct nctree* ***n***);**
-
-**int nctree_redraw(struct nctree* ***n***);**
-
-**bool nctree_offer_input(struct nctree* ***n***, const ncinput* ***ni***);**
-
-**void* nctree_focused(struct nctree* ***n***);**
-
-**void* nctree_next(struct nctree* ***n***);**
-
-**void* nctree_prev(struct nctree* ***n***);**
-
-**void* nctree_goto(struct nctree* ***n***, const int* ***spec***, size_t ***specdepth***, int* ***failspec***);**
-
-**void nctree_destroy(struct nctree* ***n***);**
-
-# DESCRIPTION
-
-**nctree**s organize static hierarchical items, and allow them to be browsed.
-Each item can have arbitrary subitems. Items can be collapsed and expanded.
-The display supports scrolling and searching. Items cannot be added or removed,
-however; they must be provided in their entirety at creation time.
-
-An **nctree** cannot be empty. **count** must be non-zero, and **items** must
-not be **NULL**. The callback function **nctreecb** must also be non-**NULL**.
-
-The callback function **nctreecb** is called on tree items when the tree is
-redrawn. It will be called on each visible item, and any item which has become
-hidden. If the item is newly hidden, the **ncplane** argument will be **NULL**.
-If the item is newly visible, the **ncplane** argument will be an empty plane.
-If the item was already visible, the **ncplane** argument will be the same
-plane passed before. If the item has not changed since the last time the
-callback was invoked, there is no need to change the plane, and the callback
-can return immediately. Otherwise, the plane ought be drawn by the callback.
-Any unused rows ought be trimmed away using **ncplane_resize**. If the plane
-is expanded in the callback, it will be shrunk back down by the widget. The
-**int** parameter indicates distance from the focused item. If the parameter
-is negative, the item is before the focused item; a positive parameter implies
-that the item follows the focused item; the focused item itself is passed zero.
-
-# RETURN VALUES
-
-**nctree_create** returns **NULL** for invalid options. This includes a **NULL**
-**items** or **nctreecb** field, or a zero **count** field.
-
-**nctree_next** and **nctree_prev** both return the **curry** pointer from the
-newly-focused item. **nctree_focused** returns the **curry** pointer from the
-already-focused item.
-
-# NOTES
-
-**nctree** shares many properties with **notcurses_reel**. Unlike the latter,
-**nctree**s support arbitrary hierarchical levels, but they do not allow
-elements to come and go across the lifetime of the widget.
-
-# SEE ALSO
-
-**notcurses(3)**,
-**notcurses_input(3)**,
-**notcurses_plane(3)**,
-**notcurses_reel(3)**
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index 757af9ffea..eb795f94fc 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -55,7 +55,6 @@ struct ncreader; // widget supporting free string input ala readline
struct ncfadectx; // context for a palette fade operation
struct nctablet; // grouped item within an ncreel
struct ncreel; // hierarchical block-based data browser
-struct nctree; // hierarchical line-based data browser
// we never blit full blocks, but instead spaces (more efficient) with the
// background set to the desired foreground.
@@ -2998,72 +2997,6 @@ API bool ncmultiselector_offer_input(struct ncmultiselector* n, const ncinput* n
// Destroy the ncmultiselector.
API void ncmultiselector_destroy(struct ncmultiselector* n);
-// nctree widget -- a vertical browser supporting line-based hierarchies.
-//
-// each item can have subitems, and has a curry. there is one callback for the
-// entirety of the nctree. visible items have the callback invoked upon their
-// curry and an ncplane. the ncplane can be reused across multiple invocations
-// of the callback.
-
-// each item has a curry, and zero or more subitems.
-typedef struct nctree_item {
- void* curry;
- struct nctree_item* subs;
- unsigned subcount;
-} nctree_item;
-
-typedef struct nctree_options {
- const nctree_item* items; // top-level nctree_item array
- unsigned count; // size of |items|
- uint64_t bchannels; // base channels
- int (*nctreecb)(struct ncplane*, void*, int); // item callback function
- uint64_t flags; // bitfield of NCTREE_OPTION_*
-} nctree_options;
-
-// |opts| may *not* be NULL, since it is necessary to define a callback
-// function.
-API ALLOC struct nctree* nctree_create(struct ncplane* n, const nctree_options* opts)
- __attribute__ ((nonnull (1, 2)));
-
-// Returns the ncplane on which this nctree lives.
-API struct ncplane* nctree_plane(struct nctree* n)
- __attribute__ ((nonnull (1)));
-
-// Redraw the nctree 'n' in its entirety. The tree will be cleared, and items
-// will be lain out, using the focused item as a fulcrum. Item-drawing
-// callbacks will be invoked for each visible item.
-API int nctree_redraw(struct nctree* n)
- __attribute__ ((nonnull (1)));
-
-// Offer input 'ni' to the nctree 'n'. If it's relevant, this function returns
-// true, and the input ought not be processed further. If it's irrelevant to
-// the tree, false is returned. Relevant inputs include:
-// * a mouse click on an item (focuses item)
-// * a mouse scrollwheel event (srolls tree)
-// * up, down, pgup, or pgdown (navigates among items)
-API bool nctree_offer_input(struct nctree* n, const ncinput* ni)
- __attribute__ ((nonnull (1, 2)));
-
-// Return the focused item, if any items are present. This is not a copy;
-// be careful to use it only for the duration of a critical section.
-API void* nctree_focused(struct nctree* n) __attribute__ ((nonnull (1)));
-
-// Change focus to the next item.
-API void* nctree_next(struct nctree* n) __attribute__ ((nonnull (1)));
-
-// Change focus to the previous item.
-API void* nctree_prev(struct nctree* n) __attribute__ ((nonnull (1)));
-
-// Go to the item specified by the array |spec| having |specdepth| elements. If
-// the spec is invalid, NULL is returned, and the depth of the first invalid
-// spec is written to *|failspec|. Otherwise, |specdepth| is written to
-// *|failspec|, and the curry is returned (|failspec| is necessary because the
-// curry could itself be NULL).
-API void* nctree_goto(struct nctree* n, const int* spec, size_t specdepth, int* failspec);
-
-// Destroy the nctree.
-API void nctree_destroy(struct nctree* n);
-
// Menus. Horizontal menu bars are supported, on the top and/or bottom rows.
// If the menu bar is longer than the screen, it will be only partially
// visible. Menus may be either visible or invisible by default. In the event of
diff --git a/src/lib/tree.c b/src/lib/tree.c
deleted file mode 100644
index c628dc4c79..0000000000
--- a/src/lib/tree.c
+++ /dev/null
@@ -1,264 +0,0 @@
-#include "internal.h"
-
-// these are never allocated themselves, but always as arrays of object
-typedef struct nctree_int_item {
- void* curry;
- ncplane* ncp;
- unsigned subcount;
- struct nctree_int_item* subs;
-} nctree_int_item;
-
-typedef struct nctree {
- int (*cbfxn)(ncplane*, void*, int);
- nctree_int_item items; // topmost set of items, holds widget plane
- unsigned* currentpath; // array of |maxdepth|+1 elements, ended by UINT_MAX
- unsigned maxdepth; // binds the path length
- int activerow; // active row 0 <= activerow < dimy
- uint64_t bchannels; // border glyph channels
-} nctree;
-
-// recursively free an array of nctree_int_item; nctree_int_item structs are
-// never individually free()d, just their innards
-static void
-free_tree_items(nctree_int_item* iarray){
- for(unsigned c = 0 ; c < iarray->subcount ; ++c){
- free_tree_items(&iarray->subs[c]);
- }
- ncplane_destroy(iarray->ncp);
- free(iarray->subs);
-}
-
-// allocates a |count|-sized array of nctree_int_items, and fills |fill| in,
-// using |items|. updates |*maxdepth| when appropriate.
-static int
-dup_tree_items(nctree_int_item* fill, const nctree_item* items, unsigned count, unsigned depth,
- unsigned* maxdepth){
- fill->subcount = count;
- fill->subs = malloc(sizeof(*fill->subs) * count);
- if(fill->subs == NULL){
- return -1;
- }
- for(unsigned c = 0 ; c < fill->subcount ; ++c){
- nctree_int_item* nii = &fill->subs[c];
- nii->curry = items[c].curry;
- if(nii->curry == NULL){
- while(c--){
- free_tree_items(&fill->subs[c]);
- }
- free(fill->subs);
- return -1;
- }
- nii->ncp = NULL;
- if(dup_tree_items(nii, items[c].subs, items[c].subcount, depth + 1, maxdepth)){
- while(c--){
- free_tree_items(&fill->subs[c]);
- }
- free(fill->subs);
- return -1;
- }
- }
- if(depth > *maxdepth){
- *maxdepth = depth;
- }
- return 0;
-}
-
-// the initial path ought point to the first item. maxdepth must be set.
-static int
-prep_initial_path(nctree* n){
- n->currentpath = malloc(sizeof(*n->currentpath) * (n->maxdepth + 1));
- if(n->currentpath == NULL){
- return -1;
- }
- const nctree_int_item* nii = &n->items;
- int c = 0;
- while(nii->subcount){
- n->currentpath[c++] = 0;
- nii = &nii->subs[0];
- }
- n->currentpath[c] = UINT_MAX;
- return 0;
-}
-
-static nctree*
-nctree_inner_create(ncplane* n, const struct nctree_options* opts){
- nctree* ret = malloc(sizeof(*ret));
- if(ret){
- ret->bchannels = opts->bchannels;
- ret->cbfxn = opts->nctreecb;
- ret->maxdepth = 0;
- if(dup_tree_items(&ret->items, opts->items, opts->count, 0, &ret->maxdepth)){
- free(ret);
- return NULL;
- }
-//fprintf(stderr, "MAXDEPTH: %u\n", ret->maxdepth);
- if(prep_initial_path(ret)){
- free_tree_items(&ret->items);
- free(ret);
- return NULL;
- }
- ret->items.ncp = n;
- ret->items.curry = NULL;
- ret->activerow = 0;
- nctree_redraw(ret);
- }
- return ret;
-}
-
-nctree* nctree_create(ncplane* n, const struct nctree_options* opts){
- notcurses* nc = ncplane_notcurses(n);
- if(opts->flags){
- logwarn(nc, "Passed invalid flags 0x%016jx\n", (uint64_t)opts->flags);
- goto error;
- }
- if(opts->count == 0 || opts->items == NULL){
- logerror(nc, "Can't create empty tree\n");
- goto error;
- }
- if(opts->nctreecb == NULL){
- logerror(nc, "Can't use NULL callback\n");
- goto error;
- }
- nctree* ret = nctree_inner_create(n, opts);
- if(ret == NULL){
- goto error;
- }
- return ret;
-
-error:
- ncplane_destroy(n);
- return NULL;
-}
-
-void nctree_destroy(nctree* n){
- if(n){
- free_tree_items(&n->items);
- free(n);
- }
-}
-
-// Returns the ncplane on which this nctree lives.
-ncplane* nctree_plane(nctree* n){
- return n->items.ncp;
-}
-
-// the prev is either:
-// the item to the left, if the last path component is 0, or
-// a drop from the rightmost non-zero path component, extended out to the right, or
-// the current item
-// so we can always just go to the last path component, act there, and possibly
-// extend it out to the maximal topright.
-void* nctree_prev(nctree* n){
- nctree_int_item* nii = &n->items;
- nctree_int_item* wedge = NULL; // tracks the rightmost non-zero path
- int idx = 0;
- while(n->currentpath[idx] != UINT_MAX){
- nii = &nii->subs[n->currentpath[idx]];
- if(idx == 0){
- wedge = &n->items;
- }else{// if(idx > 1){
- wedge = &wedge->subs[n->currentpath[idx - 1]];
- }
- ++idx;
- }
- if(n->currentpath[idx - 1]){
- --n->currentpath[idx - 1];
- nii = &wedge->subs[n->currentpath[idx - 1]];
- while(nii->subcount){
- n->currentpath[idx - 1] = nii->subcount - 1;
- nii = &nii->subs[n->currentpath[idx - 1]];
- ++idx;
- }
- n->currentpath[idx] = UINT_MAX;
- return nii->curry;
- }
- if(wedge == &n->items){
- return nii->curry; // no change
- }
- n->currentpath[idx - 1] = UINT_MAX;
- return wedge->curry;
-}
-
-// the next is either:
-// - an extension to the right, if subs are available, or
-// - a bump to the rightmost path component with subcount available, or
-// - the current item
-void* nctree_next(nctree* n){
- nctree_int_item* nii = &n->items;
- nctree_int_item* wedge = NULL; // tracks the rightmost with room in subs
- int idx = 0;
- int wedidx = 0;
- while(n->currentpath[idx] != UINT_MAX){
- if(n->currentpath[idx] < nii->subcount - 1){
- wedge = nii;
- wedidx = idx;
- }
- nii = &nii->subs[n->currentpath[idx]];
- ++idx;
- }
- // FIXME update n->activerow, redraw
- if(nii->subcount){
- n->currentpath[idx] = 0;
- n->currentpath[idx + 1] = UINT_MAX;
- return nii->subs[n->currentpath[idx]].curry;
- }
- if(wedge){
- ++n->currentpath[wedidx];
- n->currentpath[wedidx + 1] = UINT_MAX;
- return wedge->subs[n->currentpath[wedidx]].curry;
- }
- return nii->curry;
-}
-
-int nctree_redraw(nctree* n){
- ncplane* ncp = n->items.ncp;
- if(ncplane_cursor_move_yx(ncp, n->activerow, 0)){
- return -1;
- }
- // FIXME start at n->activerow with the currentpath. for each, until we run
- // out or fill the screen, check that it has an ncplane defined. if not,
- // create one. pass it to the callback with the curry.
- return 0;
-}
-
-bool nctree_offer_input(nctree* n, const ncinput* ni){
- if(ni->id == NCKEY_UP){
- nctree_prev(n);
- return true;
- }else if(ni->id == NCKEY_DOWN){
- nctree_next(n);
- return true;
- }else if(ni->id == NCKEY_PGUP){
- nctree_prev(n); // more FIXME
- return true;
- }else if(ni->id == NCKEY_PGDOWN){
- nctree_next(n); // more FIXME
- return true;
- }else if(ni->id == NCKEY_HOME){
- nctree_prev(n); // more FIXME
- return true;
- }else if(ni->id == NCKEY_END){
- nctree_next(n); // more FIXME
- return true;
- }
- // FIXME implement left, right, +, - (expand/collapse)
- return false;
-}
-
-void* nctree_focused(nctree* n){
- int idx = 0;
- const nctree_int_item* nii = &n->items;
- while(n->currentpath[idx] != UINT_MAX){
- assert(n->currentpath[idx] < nii->subcount);
- nii = &nii->subs[n->currentpath[idx]];
- ++idx;
- }
-//fprintf(stderr, "FOCUSED: %s\n", nii->curry);
- return nii->curry;
-}
-
-/*
-void* nctree_goto(nctree* n, const int* spec, size_t specdepth, int* failspec){
- // FIXME
-}
-*/
diff --git a/src/tests/tree.cpp b/src/tests/tree.cpp
deleted file mode 100644
index 4cbc840a98..0000000000
--- a/src/tests/tree.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-#include "main.h"
-#include
-
-int treecb(struct ncplane* n, void* curry, int pos){
- ncplane_printf_yx(n, 0, 0, "item: %s pos: %d",
- static_cast(curry), pos);
- return 0;
-}
-
-TEST_CASE("Tree") {
- auto nc_ = testing_notcurses();
- if(!nc_){
- return;
- }
- struct ncplane* n_ = notcurses_stdplane(nc_);
- REQUIRE(n_);
- REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
-
- // should be refused with a null items
- SUBCASE("BadTreeNoItems") {
- struct nctree_options opts = {
- .items = nullptr,
- .count = 2,
- .bchannels = 0,
- .nctreecb = treecb,
- .flags = 0,
- };
- auto treen = nctree_create(n_, &opts);
- REQUIRE(nullptr == treen);
- }
-
- // should be refused with a zero count
- SUBCASE("BadTreeNoCount") {
- struct nctree_options opts = {
- .items = {},
- .count = 0,
- .bchannels = 0,
- .nctreecb = treecb,
- .flags = 0,
- };
- auto treen = nctree_create(n_, &opts);
- REQUIRE(nullptr == treen);
- }
-
- nctree_item subs[] = {
- {
- .curry = strdup("sub1-0"),
- .subs = nullptr,
- .subcount = 0,
- },{
- .curry = strdup("sub1-1"),
- .subs = nullptr,
- .subcount = 0,
- }
- };
-
- nctree_item items[] = {
- {
- .curry = strdup("item0"),
- .subs = nullptr,
- .subcount = 0,
- }, {
- .curry = strdup("item1"),
- .subs = subs,
- .subcount = 2,
- },
- };
-
- // should be refused with a null callback
- SUBCASE("BadTreeNoCallback") {
- struct nctree_options opts = {
- .items = items,
- .count = sizeof(items) / sizeof(*items),
- .bchannels = 0,
- .nctreecb = nullptr,
- .flags = 0,
- };
- auto treen = nctree_create(n_, &opts);
- REQUIRE(nullptr == treen);
- }
-
- SUBCASE("Create") {
- struct nctree_options opts = {
- .items = items,
- .count = sizeof(items) / sizeof(*items),
- .bchannels = 0,
- .nctreecb = treecb,
- .flags = 0,
- };
- const ncplane_options nopts = {
- .y = 0, .x = 0, .rows = 3, .cols = ncplane_dim_y(n_),
- .userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0,
- };
- auto treen = ncplane_create(n_, &nopts);
- REQUIRE(nullptr != treen);
- auto tree = nctree_create(treen, &opts);
- REQUIRE(nullptr != tree);
- CHECK(0 == notcurses_render(nc_));
- CHECK(treen == nctree_plane(tree));
- CHECK(items[0].curry == nctree_focused(tree));
- nctree_destroy(tree);
- }
-
- SUBCASE("Traverse") {
- struct nctree_options opts = {
- .items = items,
- .count = sizeof(items) / sizeof(*items),
- .bchannels = 0,
- .nctreecb = treecb,
- .flags = 0,
- };
- const ncplane_options nopts = {
- .y = 0, .x = 0, .rows = 3, .cols = ncplane_dim_y(n_),
- .userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0,
- };
- auto treen = ncplane_create(n_, &nopts);
- REQUIRE(nullptr != treen);
- auto tree = nctree_create(treen, &opts);
- REQUIRE(nullptr != tree);
- CHECK(0 == notcurses_render(nc_));
- CHECK(treen == nctree_plane(tree));
- CHECK(items[0].curry == nctree_focused(tree));
-
- CHECK(items[0].curry == nctree_prev(tree));
- CHECK(items[0].curry == nctree_focused(tree));
-
- CHECK(items[1].curry == nctree_next(tree));
- CHECK(items[1].curry == nctree_focused(tree));
-
- CHECK(items[1].subs[0].curry == nctree_next(tree));
- CHECK(items[1].subs[0].curry == nctree_focused(tree));
-
- CHECK(items[1].subs[1].curry == nctree_next(tree));
- CHECK(items[1].subs[1].curry == nctree_focused(tree));
-
- CHECK(items[1].subs[1].curry == nctree_next(tree));
- CHECK(items[1].subs[1].curry == nctree_focused(tree));
-
- CHECK(items[1].subs[0].curry == nctree_prev(tree));
- CHECK(items[1].subs[0].curry == nctree_focused(tree));
-
- CHECK(items[1].curry == nctree_prev(tree));
- CHECK(items[1].curry == nctree_focused(tree));
-
- CHECK(items[0].curry == nctree_prev(tree));
- CHECK(items[0].curry == nctree_focused(tree));
-
- CHECK(items[0].curry == nctree_prev(tree));
- CHECK(items[0].curry == nctree_focused(tree));
- nctree_destroy(tree);
- }
-
- CHECK(0 == notcurses_stop(nc_));
-}