Menu Browsing

I l @ ve RuBoard

Menu Browsing

Many computer programs use menus as part of the user interface. Menus make programs easier for the user, but they do pose some problems for the programmer. Let's see what's involved.

A menu offers the user a choice of responses. Here's a hypothetical example:

 Enter the letter of your choice: a. advice           b. bell c. count            q. quit 

Ideally, the user then enters one of these choices, and the program acts on that choice. As a programmer, you want to make this process go smoothly. The first subgoal is for the program to work smoothly when the user follows instructions. The second subgoal is for the program to work smoothly when the user fails to follow instructions. As you might expect, the second goal is the more difficult because it's hard to anticipate all the possible mistreatment that might come your program's way.

Tasks

Let's get more specific and look at the tasks the program needs to perform. It needs to get the user's response, and it needs to select a course of action based on the response. Also, the program should provide a way to return to the menu for further choices. C's switch statement is a natural vehicle for choosing actions because each user choice can be made to correspond to a particular case label. You can use a while statement to provide repeated access to the menu. In pseudocode, you can describe the process this way:

 get choice while choice is not 'q'     switch to desired choice and execute it     get next choice 

Toward a Smoother Execution

The goals of program smoothness come into play when you decide how to implement this plan. One thing you can do, for example, is have the "get choice" segment screen out inappropriate responses so that only correct responses are passed on to the switch . That suggests representing the input process with a function that can return only correct responses. Combining that with a while loop and a switch leads to the following program structure:

 #include <stdio.h> int getchoice(void); void count(void); int main(void) {     int choice;     while ( (choice = getchoice()) != 'q')     {         switch (choice)         {             case 'a' :  printf("Buy low, sell high.\n");                         break;             case 'b' :  putchar('\a');  /* ANSI */                         break;             case 'c' :  count();                         break;             default  :  printf("Program error!\n");                         break;         }     }     return 0; } 

The getchoice() function is defined so that it can return only the values 'a' , 'b' , 'c' , and 'q' . You use it much as you use getchar () : getting a value and comparing it to a termination value, 'q' , in this case. We've kept the actual menu choices simple so that you can concentrate on the program structure; we'll get to the count() function soon. The default case is handy for debugging. If the getchoice() function fails to limit its return value to the intended values, the default case lets you know something fishy is going on.

The getchoice() Function

Here, in pseudocode, is one possible design for this function:

 show choices get response while response is not acceptable     prompt for more response     get response 

And here is a simple, but awkward , implementation:

 int getchoice(void) {     int ch;     printf("Enter the letter of your choice:\n");     printf("a. advice           b. bell\n");     printf("c. count            d. quit\n");     ch = getchar();     while (  (ch < 'a'  ch > 'c') && ch != 'q')     {         printf("Please respond with a, b, c, or q.\n");         ch = getchar();     }     return ch; } 

The problem is that with buffered input, every newline generated by the Return key is treated as an erroneous response. To make the program interface smoother, the function should skip over newlines.

There are several ways to do that. One is to replace getchar() with a new function called getfirst() that reads the first character on a line and discards the rest. This method also has the advantage of treating an input line consisting of, say, act , as being the same as a simple a instead of treating it as one good response followed by c for count . With this goal in mind, you can reprogram the input function as follows:

 int getchoice(void) {     int ch;     int getfirst(void);     printf("Enter the letter of your choice:\n");     printf("a. advice           b. bell\n");     printf("c. count            q. quit\n");     ch = getfirst();     while (  (ch < 'a'  ch > 'c') && ch != 'q')     {         printf("Please respond with a, b, c, or q.\n");         ch = getfirst();     }     return ch; } int getfirst(void) {     int ch;     ch = getchar();           /* read next character */     while (getchar() != '\n')         continue;             /* skip rest of line */     return ch; } 

Mixing Character and Numeric Input

As you saw in Listing 8.8, mixing character and numeric input poses problems, and that is true with the menu example, too. Suppose, for instance, the count() function (choice c ) were to look like this:

 void count(void) {     int n,i;     printf("Count how far? Enter an integer:\n");     scanf("%d", &n);     for (i = 1; i <= n; i++)         printf("%d\n", i); } 

If you then responded by entering 3 , scanf() would read the 3 and leave a newline character as the next character in the input queue. The next call to getchoice() would result in getfirst() returning this newline character, leading to undesirable behavior.

One way to fix that problem is to rewrite getfirst() so that it returns the next non-whitespace character rather than just the next character encountered . We leave that as an exercise for the reader. A second approach is have the count() function tidy up and clear the newline itself. This is the approach to take:

 void count(void) {     int n,i;     printf("Count how far? Enter an integer:\n");     if (scanf("%d", &n) != 1)     {         printf("Please use digits next time; this time");         printf(" I'll use the integer 5.\n");         n = 5;     }     for (i = 1; i <= n; i++)         printf("%d\n", i);     while ( getchar() != '\n')         continue;       /* clear newline, bad input */ } 

You use the return value of scanf() to detect non-numeric input, too. To simplify matters, have the program substitute a default value for n if the input is bad. Also, in that case, the while loop at the end of the function disposes of the bad input. If you are more ambitious, you can have the function prompt for new input instead of using a default value. We show an example in Chapter 9. Listing 8.9 shows the final menu program. Note that function prototypes can appear either inside or outside function definitions. The main requirement for placement is that the prototype appears before the function is first used.

Listing 8.9 The menuette.c program.
 /* menuette.c -- menu techniques */ #include <stdio.h> int getchoice(void); int getfirst(void); void count(void); int main(void) {     int choice;     int getchoice(void);     void count(void);     while ( (choice = getchoice()) != 'q')     {         switch (choice)         {             case 'a' :  printf("Buy low, sell high.\n");                         break;             case 'b' :  putchar('\a');  /* ANSI */                         break;             case 'c' :  count();                         break;             default  :  printf("Program error!\n");                         break;         }     }     return 0; } void count(void) {     int n,i;     printf("Count how far? Enter an integer:\n");     if (scanf("%d", &n) != 1)     {         printf("Please use digits next time; this time");         printf(" I'll use the integer 5.\n");         n = 5;     }     for (i = 1; i <= n; i++)         printf("%d\n", i);     while ( getchar() != '\n')         continue; } int getchoice(void) {     int ch;     printf("Enter the letter of your choice:\n");     printf("a. advice           b. bell\n");     printf("c. count            q. quit\n");     ch = getfirst();     while (  (ch < 'a'  ch > 'c') && ch != 'q')     {         printf("Please respond with a, b, c, or q.\n");         ch = getfirst();     }     return ch; } int getfirst(void) {     int ch;     ch = getchar();     while (getchar() != '\n')         continue;     return ch; } 

Here is a sample run:

 Enter the letter of your choice: a. advice           b. bell c. count            q. quit  a  Buy low, sell high. Enter the letter of your choice: a. advice           b. bell c. count            q. quit  count  Count how far? Enter an integer: two Please use digits next time; this time I'll use the integer 5. 1 2 3 4 5 Enter the letter of your choice: a. advice           b. bell c. count            q. quit  d  Please respond with a, b, c, or q.  q  

It can be hard work getting a menu interface to work as smoothly as you might want, but after you develop a viable approach, you can reuse it in a variety of situations.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net