#include #include #include /* menu - show a menu, allowing selection by cursor movement or inputting a number menu title item [ item ]... If there are more than 9 items and a number 1 is input, wait for either a second digit or Enter Cater for showing in two columns if there are too many items for one Compiling with cc: cc -lcurses -o menu menu.c */ #define TITLE_ROW 3 #define TOP_ROW 5 int entries, first_x, first_y, menurows, menucols, colwidth; int main(int argc, char *argv[]) { WINDOW *mywin; if (argc < 3) { printf("No menu entries supplied - should be title, entries"); return 1; } int maxlen; int ch, numch, count, in_num; int rows, cols, x, y, key_x, key_y, curr; char msg[80]; bool valid_in; attr_t currattr; /* Initialise screen */ mywin = initscr(); keypad( mywin, TRUE ); start_color(); noecho(); /* Number of menu entries not including title */ entries = argc - 2; /* Check screen size & whether we need 1, 2, or 3 cols */ getmaxyx( mywin, rows, cols ); if (entries > ((rows - (TOP_ROW + 2)) * 3)) { printf("Too many menu entries"); return 1; } if (entries > (rows - (TOP_ROW + 2)) * 2) { menucols = 3; menurows = (entries + 2) / 3; } else if (entries > (rows - (TOP_ROW + 2))) { menucols = 2; menurows = (entries + 1) / 2; } else { menucols = 1; menurows = entries; } /* If we have colour handling, use that, otherwise underline */ if ( ! can_change_color() ) { currattr = A_UNDERLINE; } else { init_pair(1, COLOR_CYAN, COLOR_BLACK); currattr = COLOR_PAIR(1); } /* What's the longest menu title? */ colwidth = cols / menucols; maxlen = 0; for ( count = 2; count < argc; count++ ) { if ( strlen(argv[count]) > maxlen ) { maxlen = strlen(argv[count]); } } /* x and y for the start of the first menu entry */ /* Allow for line no */ first_x = (((cols / menucols) - maxlen) / 2) - 4; first_y = TOP_ROW - 1; if ( first_x < 0 ) { first_x = 0; } /* Write title */ x = ((cols - strlen(argv[1])) / 2) - 1; y = TITLE_ROW - 1; wmove( mywin, y, x); wattron( mywin, A_REVERSE ); wprintw( mywin, argv[1]); wattroff( mywin, A_REVERSE ); /* Show all menu items */ for ( count = 2; count < argc; count++ ) { curr = count - 1; x = get_x_for( curr ); y = get_y_for( curr ); mvwprintw( mywin, y++, x, "%d - %s", curr, argv[count], x ); } /* Show instruction line */ sprintf( msg, "1 - %d - choose option, Esc or Q - quit", entries ); x = ((cols - strlen( msg )) / 2) - 1; y = first_y + menurows + 1; mvwprintw( mywin, y, x, msg ); refresh(); /* Position for first keypress, initialise values */ key_x = x + strlen( msg ) + 1; key_y = y; valid_in = FALSE; curr = 1; in_num = 0; do { x = get_x_for( curr ); /* Position for menu item */ y = get_y_for( curr ); wattron( mywin, currattr ); /* Print curr line highlight */ mvwprintw( mywin, y, x, "%d - %s", curr, argv[curr + 1], x ); wattroff( mywin, currattr ); refresh(); ch = wgetch( mywin ); numch = ch - '0'; /* Actual number input */ if ( numch >= 0 && numch <= 9 ) { if ( in_num > 0 ) { /* Digit already input */ numch = numch + ( in_num * 10 ); if ( numch <= entries ) { valid_in = TRUE; break; } else { mvwprintw( mywin, key_y, key_x, "%d - not entry", numch ); in_num = 0; continue; } } else { /* Could this be first of 2 digits? */ if ( numch <= ( entries / 10 ) ) { in_num = numch; mvwprintw( mywin, key_y, key_x, "%d ", numch ); continue; } if ( numch > 0 && numch <= entries ) { valid_in = TRUE; break; } } } mvwprintw( mywin, key_y, key_x, " " ); switch( ch ) { case 'Q' : case 'q' : case 27 : valid_in = TRUE; numch = 0; break; case 10 : /* Enter key */ if ( in_num > 0 ) { /* Number's been input */ numch = in_num; in_num = 0; if ( numch > entries ) { continue; } } else { /* Select current line */ numch = curr; } ch = '0' + numch; /* Convert to ASCII number */ valid_in = TRUE; break; case KEY_DOWN : if ( curr < entries ) { mvwprintw( mywin, y, x, "%d - %s", curr, argv[curr + 1] ); curr++; } break; case KEY_UP : if ( curr > 1 ) { mvwprintw( mywin, y, x, "%d - %s", curr, argv[curr + 1] ); curr--; } break; case KEY_LEFT : if ( menucols > 1 && curr > menurows ) { mvwprintw( mywin, y, x, "%d - %s", curr, argv[curr + 1] ); curr = curr - menurows; } break; case KEY_RIGHT : if ( menucols > 1 && ( curr + menurows <= entries ) ) { mvwprintw( mywin, y, x, "%d - %s", curr, argv[curr + 1] ); curr = curr + menurows; } break; /* default : mvwprintw( mywin, 23, 10, "%d", ch ); */ } } while ( ! valid_in ); wclear( mywin ); refresh(); endwin(); return numch; } int get_x_for ( int entry ) { int x, col; col = (entry - 1) / menurows; /* col starts at 0 for simplicity */ if ( entries > 9 && entry < 10 ) { x = first_x + 1; /* Offset right if 1 digit */ } else { x = first_x; } x = x + (col * colwidth); return x; } int get_y_for ( int entry ) { int y, col; col = (entry - 1) / menurows; /* col starts at 0 for simplicity */ y = (first_y - 1) + entry; y = y - (col * menurows); return y; }