menu: reject empty sections #179

pull/319/head
nick black 5 years ago
parent 5cf1bba5f5
commit 5cd4fc9a1f
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -19,43 +19,51 @@ free_menu_sections(ncmenu* ncm){
static int static int
dup_menu_section(struct ncmenu_section* dst, const struct ncmenu_section* src){ dup_menu_section(struct ncmenu_section* dst, const struct ncmenu_section* src){
// we must reject any empty section
if(src->itemcount == 0 || src->items == NULL){
return -1;
}
dst->bodycols = 0; dst->bodycols = 0;
dst->itemselected = 0; dst->itemselected = 0;
dst->items = NULL; dst->items = NULL;
if( (dst->itemcount = src->itemcount) ){ // we must reject any section which is entirely separators
dst->items = malloc(sizeof(*dst->items) * src->itemcount); bool gotitem = false;
if(dst->items == NULL){ dst->itemcount = src->itemcount;
return -1; dst->items = malloc(sizeof(*dst->items) * src->itemcount);
} if(dst->items == NULL){
for(int i = 0 ; i < src->itemcount ; ++i){ return -1;
if(src->items[i].desc){ }
if((dst->items[i].desc = strdup(src->items[i].desc)) == NULL){ for(int i = 0 ; i < src->itemcount ; ++i){
while(--i){ if(src->items[i].desc){
free(&dst->items[i].desc); if((dst->items[i].desc = strdup(src->items[i].desc)) == NULL){
} while(i--){
free(dst->items); free(&dst->items[i].desc);
return -1;
}
const int cols = mbswidth(dst->items[i].desc);
if(cols > dst->bodycols){
dst->bodycols = cols;
} }
}else{ free(dst->items);
dst->items[i].desc = NULL; return -1;
} }
gotitem = true;
const int cols = mbswidth(dst->items[i].desc);
if(cols > dst->bodycols){
dst->bodycols = cols;
}
}else{
dst->items[i].desc = NULL;
} }
} }
if(!gotitem){
while(--dst->itemcount){
free(&dst->items[dst->itemcount].desc);
}
free(dst->items);
return -1;
}
return 0; return 0;
} }
// Duplicates all menu sections in opts, adding their length to '*totalwidth'. // Duplicates all menu sections in opts, adding their length to '*totalwidth'.
static int static int
dup_menu_sections(ncmenu* ncm, const ncmenu_options* opts, int* totalwidth, int* totalheight){ dup_menu_sections(ncmenu* ncm, const ncmenu_options* opts, int* totalwidth, int* totalheight){
ncm->sections = NULL;
if((ncm->sectioncount = opts->sectioncount) == 0){
++*totalwidth; // one character margin on right
return 0;
}
ncm->sections = malloc(sizeof(*ncm->sections) * opts->sectioncount); ncm->sections = malloc(sizeof(*ncm->sections) * opts->sectioncount);
if(ncm->sections == NULL){ if(ncm->sections == NULL){
return -1; return -1;
@ -65,13 +73,14 @@ dup_menu_sections(ncmenu* ncm, const ncmenu_options* opts, int* totalwidth, int*
for(int i = 0 ; i < opts->sectioncount ; ++i){ for(int i = 0 ; i < opts->sectioncount ; ++i){
int cols = mbswidth(opts->sections[i].name); int cols = mbswidth(opts->sections[i].name);
if(cols < 0 || (ncm->sections[i].name = strdup(opts->sections[i].name)) == NULL){ if(cols < 0 || (ncm->sections[i].name = strdup(opts->sections[i].name)) == NULL){
while(--i){ while(i--){
free_menu_section(&ncm->sections[i]); free_menu_section(&ncm->sections[i]);
} }
return -1;
} }
if(dup_menu_section(&ncm->sections[i], &opts->sections[i])){ if(dup_menu_section(&ncm->sections[i], &opts->sections[i])){
free(ncm->sections[i].name); free(ncm->sections[i].name);
while(--i){ while(i--){
free_menu_section(&ncm->sections[i]); free_menu_section(&ncm->sections[i]);
} }
return -1; return -1;
@ -131,11 +140,7 @@ write_header(ncmenu* ncm){ ncm->ncp->channels = ncm->headerchannels;
} }
ncmenu* ncmenu_create(notcurses* nc, const ncmenu_options* opts){ ncmenu* ncmenu_create(notcurses* nc, const ncmenu_options* opts){
if(opts->sectioncount < 0){ if(opts->sectioncount <= 0 || !opts->sections){
return NULL;
}else if(opts->sectioncount == 0 && opts->sections){
return NULL;
}else if(opts->sectioncount && !opts->sections){
return NULL; return NULL;
} }
int totalheight = 1; int totalheight = 1;

@ -17,19 +17,53 @@ TEST_CASE("MenuTest") {
REQUIRE(n_); REQUIRE(n_);
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0)); REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
SUBCASE("EmptyMenuTop") { // an empty menu ought be rejected
SUBCASE("EmptyMenuTopReject") {
struct ncmenu_options opts{}; struct ncmenu_options opts{};
struct ncmenu* ncm = ncmenu_create(nc_, &opts); struct ncmenu* ncm = ncmenu_create(nc_, &opts);
REQUIRE(nullptr != ncm); REQUIRE(nullptr == ncm);
CHECK(0 == notcurses_render(nc_)); CHECK(0 == notcurses_render(nc_));
ncmenu_destroy(ncm); ncmenu_destroy(ncm);
} }
SUBCASE("EmptyMenuBottom") { SUBCASE("EmptyMenuBottomReject") {
struct ncmenu_options opts{}; struct ncmenu_options opts{};
opts.bottom = true; opts.bottom = true;
struct ncmenu* ncm = ncmenu_create(nc_, &opts); struct ncmenu* ncm = ncmenu_create(nc_, &opts);
REQUIRE(nullptr != ncm); REQUIRE(nullptr == ncm);
CHECK(0 == notcurses_render(nc_));
ncmenu_destroy(ncm);
}
// an empty section ought be rejected
SUBCASE("EmptySectionReject") {
struct ncmenu_options opts{};
struct ncmenu_section sections[] = {
{ .name = strdup("Empty"), .itemcount = 0, .items = nullptr,
.xoff = -1, .bodycols = -1, .itemselected = -1, },
};
opts.sections = sections;
opts.sectioncount = sizeof(sections) / sizeof(*sections);
struct ncmenu* ncm = ncmenu_create(nc_, &opts);
REQUIRE(nullptr == ncm);
CHECK(0 == notcurses_render(nc_));
ncmenu_destroy(ncm);
}
// a section with only separators ought be rejected
SUBCASE("SeparatorSectionReject") {
struct ncmenu_item empty_items[] = {
{ .desc = nullptr, .shortcut = {}, },
};
struct ncmenu_section sections[] = {
{ .name = strdup("Empty"), .itemcount = 1, .items = empty_items,
.xoff = -1, .bodycols = -1, .itemselected = -1, },
};
struct ncmenu_options opts{};
opts.sections = sections;
opts.sectioncount = sizeof(sections) / sizeof(*sections);
struct ncmenu* ncm = ncmenu_create(nc_, &opts);
REQUIRE(nullptr == ncm);
CHECK(0 == notcurses_render(nc_)); CHECK(0 == notcurses_render(nc_));
ncmenu_destroy(ncm); ncmenu_destroy(ncm);
} }

Loading…
Cancel
Save