I'm currently writing a function that creates a menu window with text positioned in various ways depending on parameters. When printing the contents of an array horizontally, the first call of menu() works as expected (words are printed horizontally with spaces between them) However if the function is called again, the x position of printf will not change, leading to mvwprintw overwriting the previous word. This would suggest something is wrong with my for loop, to me at least, however creating vertical menus works as expected with each word being printed under the last.
nmenu.c
#include "nmenu.h"
#include <ncurses.h>
#include <stdlib.h>
#include <string.h>
int menu(char title[], char description[], char *choices[], int choicesSize, bool vertical, bool centered)
{
int menuXMax = 0;
int highlight = 0;
int menuHeight = 9;
int menuWidth = 30;
int choicesLength = 0;
int yMax, xMax;
getmaxyx(stdscr, yMax, xMax);
for(int i; i < choicesSize; i++)
{
if(strlen(choices[i]) > choicesLength)
{
choicesLength = strlen(choices[i]);
}
}
if(vertical)
{
menuHeight = choicesSize+8;
menuWidth = choicesLength+4;
} else
{
menuWidth = choicesLength*choicesSize+choicesSize;
}
if(strlen(description) > menuWidth)
{
menuWidth = strlen(description)+4;
}
WINDOW *menu = newwin(menuHeight, menuWidth, yMax/2-menuHeight/2, xMax/2-menuWidth/2);
box(menu, 0, 0);
refresh();
wrefresh(menu);
keypad(menu, true);
wattron(menu, A_REVERSE);
mvwprintw(menu, 0, 2, title);
wattroff(menu, A_REVERSE);
mvwprintw(menu, 2, menuWidth/2-strlen(description)/2, description);
mvwprintw(menu, menuHeight-3, menuWidth/2-4, "[C]ancel");
menuXMax = getmaxx(menu);
while(true)
{
for(int i = 0; i < choicesSize; i++)
{
if(i == highlight)
{
wattron(menu, A_REVERSE);
}
if(vertical)
{
if(centered)
{
mvwprintw(menu, i+4, menuXMax/2-strlen(choices[i])/2, choices[i]);
} else
{
mvwprintw(menu, i+4, 2, choices[i]);
}
} else
{
if(centered)
{
mvwprintw(menu, 4, (menuXMax/2-choicesLength*choicesSize/2)+choicesLength*i, choices[i]);
} else
{
mvwprintw(menu, 4, 2+choicesLength*i, choices[i]);
}
}
wattroff(menu, A_REVERSE);
}
switch(wgetch(menu))
{
... // User input
}
}
MenuBreak: ;
return 0;
}
test.c
#include "nmenu.h"
#include <ncurses.h>
char *choices[4] = {"Yes", "No", "Maybe", "Sometimes"};
int main()
{
// Start ncurses
initscr();
noecho();
cbreak();
curs_set(0);
menu("Ver 1", "Choices should be uncentered and presented vertically", choices, 4, true, false);
menu("Ver 2", "Choices should be centered and presented vertically", choices, 4, true, true);
// End ncurses
getch();
endwin();
return 0;
}
test.c will output the first menu normally, with the second menu having overlapping text. This also occurs when the order of the calls are switched. I just cant see why the for loop will not iterate properly when the function is run again.
In the following line:
for(int i; i < choicesSize; i++)
the variable i is not initialized. It should be set to zero, so that it looks like:
for(int i = 0; i < choicesSize; i++)
Then choicesLength will be correctly computed and the horizontal layout also works.
Demo
A screenshot of the result looks like this then:
Undefined Behavior
Variables that are not initialized lead to undefined behavior in C and should therefore be avoided at any rate.
It is helpful to switch on compiler warnings, e.g. when using
gcc -Wextra -Wall main.c nmenu.c -o tst -lncurses
this helpful message is displayed:
nmenu.c:16:16: warning: variable 'i' is uninitialized when used here [-Wuninitialized]
for(int i; i < choicesSize; i++)
^