Search code examples
cmenuncursestui

Basic Ncurses Menu


Im trying to do a basic menu in C. I'm supposed to do this with ncurses lib. I was working with this tutorial: Video On YouTube

But mine version has some problems: 1)The menu will not print properly, it will reveal only while choosing menu items. Then the highlight won't go off 2)Option made on menu won't print on the top

Can you help me? Is that idea of Menu good or should i look for other tutorial (any help ?).

#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
int main(int argc, char **argv)
{
    int i, c;
    char powitanie[]="SLOWNIK UNIWERSALNY";
    int szer, dlug; //wartosci dlugosci i szerokosci terminalu
    initscr(); //Inizjalizacja całości ncurses, kolory itp
    raw();
    noecho();
    keypad(stdscr, TRUE);
    start_color();
    //init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
    getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
    move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
    //attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
    printw(powitanie); //Drukujemy powitanie
    //attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
    refresh();//Odswiezamy (inaczej się nie wyswietli)
    WINDOW * menuwin=newwin(6, dlug-12, szer-8, 6); //Definiujemy i tworzymy 'okno'
    box(menuwin, 0, 0);
    refresh();//ponownie odświeżamy aby okno się pojawiło
    wrefresh(menuwin);//odświeżamy samo okno
    keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
    char *opcje[] = {
                        "Tlumacz z Polskiego na Angielski",
                        "Tlumacz z Angielskiego na Polski",
                        "Edystuj slownik",
                        "Wybierz slownik",
                        "Wyjdz",
                  };
    int wybor;
    int zaznacz=0;
    while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
    {
        for(i=0; i<5; i++)
        {
            if(i==zaznacz)
            {
                wattron(menuwin, A_REVERSE);
                mvwprintw(menuwin, i+1, 1, opcje[i]);
                wattroff(menuwin, A_REVERSE);
            }
            wybor = wgetch(menuwin);
            switch(wybor)
            {
                case KEY_UP:
                zaznacz--;
                if(zaznacz==-1) zaznacz=0;//zabezpieczenie przed wyjsciem "poza" menu
                break;
                case KEY_DOWN:
                zaznacz++;
                if(zaznacz==5) zaznacz=4;
                break;
                default:
                break;
            }
            if(wybor==10) break;
        }
        printw("Wybrano:%s", opcje[zaznacz]);
    }
    return(0);
}

PS: Code comments are not in English but i hope the won't be necessary


Solution

  • There are quite a few problems here. I have included a modified version of your code that works, and I will attempt to describe the changes.

    There were some unused variables, namely argc, argv, and c, so I cast these to void in order to silence compiler warnings. You can remove the c and change to int main(void), if you like, removing these variables altogether.

    I have added the stdlib.h header file to your #includes for the exit() function. This is used in the new error function, fail(), that I added to your code. You should always check the return values of any function that you call when programming in C. Here it is particularly important to check, first if the terminal supports color with the has_colors() function, and then if the call to start_color() is successful. If either of these fail, the fail() function is called with an error message, and the program exits with the EXIT_FAILURE value. The function has_colors() returns a bool, and the start_color() function returns an int (OK if successful, otherwise ERR).

    Now that colors have been initialized, I see that the lower border of your menu selection window is being overwritten by the menu text. To fix this, I changed the size of your window, making it one line taller:

    WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6);
    

    The fundamental problem of improper printing that you reported was because of a misplaced brace in the for loop controlling the printing of the menu items. I took the opportunity to reorganize the loop a bit; now there is only one call to mvwprintw(). The A_REVERSE attribute is set before printing if the current item is also the selected item, and it is again unset after printing.

    I also changed the limit tests in the switch statement from equalities to inequalites. It is better practice to use , e.g., if (zaznacz < 0) instead of if (zaznacz == -1) in such cases. I added a newline character to the beginning of the format string in the final printw(), since some of the selections are too long to fit in the window at the end of the title. You can move this output wherever you like.

    Finally, I added a refresh() after the final printw() statement, and a getch() to wait for the user to hit ENTER before exiting the program. It is very important to cleanup by calling endwin() before exiting an NCurses program. This function reverses changes made to your terminal by NCurses while your program was running, and failure to do this can lead to terminal unpleasantries.

    #include <stdio.h>
    #include <ncurses.h>
    #include <string.h>
    #include <menu.h>
    #include <stdlib.h>           // added for exit() function
    
    void fail(char *msg) {
        endwin();
        puts(msg);
        exit(EXIT_FAILURE);
    }
    
    int main(int argc, char **argv)
    {
        /* Commandline argument currently unused */
        (void) argc;
        (void) argv;
    
        int i, c;
        (void) c;                       // c is currently unused
        char powitanie[]="SLOWNIK UNIWERSALNY";
        int szer, dlug; //wartosci dlugosci i szerokosci terminalu
    
        initscr(); //Inizjalizacja całości ncurses, kolory itp
        raw();
        noecho();
        keypad(stdscr, TRUE);
    
        /* Test to see if terminal has colors */
        if (has_colors() == false) {
            fail("Colors unavailable\n");
        }
    
        if (start_color() != OK) {
            fail("Unable to start colors\n");
        }
    
        //init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
    
        getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
        move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
        //attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
        printw(powitanie); //Drukujemy powitanie
        //attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
        refresh();//Odswiezamy (inaczej się nie wyswietli)
        WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6); //Definiujemy i tworzymy 'okno'
        box(menuwin, 0, 0);
        refresh();//ponownie odświeżamy aby okno się pojawiło
        wrefresh(menuwin);//odświeżamy samo okno
        keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
    
        char *opcje[] = {
            "Tlumacz z Polskiego na Angielski",
            "Tlumacz z Angielskiego na Polski",
            "Edystuj slownik",
            "Wybierz slownik",
            "Wyjdz",
        };
        int wybor;
        int zaznacz=0;
    
        while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
        {
            for(i = 0; i < 5; i++) {
                if(i == zaznacz)
                    wattron(menuwin, A_REVERSE);
                mvwprintw(menuwin, i+1, 1, opcje[i]);
                if (i == zaznacz)
                    wattroff(menuwin, A_REVERSE);
            }
    
            wybor = wgetch(menuwin);
            switch(wybor)
            {
            case KEY_UP:
                zaznacz--;
                if(zaznacz < 0) zaznacz = 0;//zabezpieczenie przed wyjsciem "poza" menu
                break;
            case KEY_DOWN:
                zaznacz++;
                if(zaznacz > 4) zaznacz = 4;
                break;
            default:
                break;
            }
    
            if(wybor==10) break;
        }
    
        printw("\nWybrano:%s", opcje[zaznacz]);
        refresh();
    
        /* Wait for user to press enter to exit */
        getch();
    
        /* Need to cleanup before exit */
        endwin();
    
        return 0;
    }