March 1, 2008

Implementing a Menu with Submenus in a C console application


Recently, I had to implement a menu in a C console application, where items where grouped in submenus. What I wanted to have was a possibility to see the same submenu items after I have finished executing some submenu code. To clarify this idea (as my poor english might produce a lot of mess) I will ask you to look at the below picture.



As you see, when I select "Menu 2", I see specific submenu items. After I call "Menu 2 : Submenu - 1" and the specific subroutines executes, I see the items of the first submenu. To return to the "Main menu" I need to select "Exit".


Now we will look at code. First of all we need to define constants for menu items.

const int MENU_MAIN        = 1;
const int MENU_1 = 2;
const int MENU_1_SUBMENU_1 = 3;
const int MENU_2 = 4;
const int MENU_2_SUBMENU_1 = 5;
const int MENU_2_SUBMENU_2 = 6;
const int MENU_EXIT = 7;

The main() function will call the displayMenu helper function and then execute subroutines depending on the user choice.

void main()
{
int menuOption = MENU_MAIN;
do {
menuOption = displayMenu(menuOption);
switch(menuOption) {
case MENU_1_SUBMENU_1:
do_menu_1_submenu_1_function();
menuOption = MENU_1;
break;
case MENU_2_SUBMENU_1:
do_menu_2_submenu_1_function();
menuOption = MENU_2;
break;
case MENU_2_SUBMENU_2:
do_menu_2_submenu_2_function();
menuOption = MENU_2;
break;
}
} while (menuOption != MENU_EXIT);
}


Let's discuss the above code a bit. At first we initialize menuOption variable to show the "Main menu". Then we call the displayMenu function, which returns user input. The subtle thing is the reassigning menuOption after executing some routines based on the user choice. This lets us show the same submenu items from which we selected our subroutine.

Now we have to show the function, which accepts user input.


int displayMenu(int startMenu)
{
int userChoice = -1;
switch(startMenu) {
case MENU_MAIN:
// display main menu
printf("-- Main Menu --\n");
printf( "1. Menu - 1\n2. Menu - 2\n3. Exit\n\n" );
while( userChoice<1>3 ) {
printf( "Please choose an option (1-3): " );
scanf( "%d", &userChoice );
}
// process user choice
switch(userChoice) {
case 1: return displayMenu(MENU_1);
case 2: return displayMenu(MENU_2);
case 3: return MENU_EXIT;
}
// we should never come to this point
assert(0);
case MENU_1:
// display submenu 1
printf( "\t-- Menu 1 --\n");
printf( "\t1. Menu 1 : SubMenu - 1\n\t2. Exit\n\n" );
while( userChoice<1>2 ) {
printf( "\tPlease choose an option (1-2): " );
scanf( "%d", &userChoice );
}
// process user choice
switch(userChoice) {
case 1: return MENU_1_SUBMENU_1;
case 2: return MENU_MAIN;
}
// we should never come to this point
assert(0);
case MENU_2:
// similar to case MENU_1
}
}


At the beginning the user sees "Main menu". When he selects some submenu, the displayMenu function makes a recursive call with a specific start submenu:


// process user choice
switch(userChoice) {
case 1: return displayMenu(MENU_1);
case 2: return displayMenu(MENU_2);
case 3: return MENU_EXIT;


As you see, the displayMenu function can only return one of the following values: {MENU_1_SUBMENU_1, MENU_2_SUBMENU_1, MENU_2_SUBMENU_2, MENU_EXIT }. Actually, all the other values, like MENU_MAIN, MENU_1 and MENU_2 denote only the current manu layer (i.e. top menu or submenus) and do not result in executing some specific business logic. That is why in the main() function we get from the displayMenu() functions only these values, which carry meaningful user input (i.e. MENU_1_SUBMENU_1, MENU_2_SUBMENU_1, MENU_2_SUBMENU_2, MENU_EXIT).


With this technique you can have any number of layered submenus easily; and all the mess is hidden in the displayMenu function :-).

The complete source code for this example can be downloaded here



http://www.linkedin.com/in/oldbam

No comments: