Advanced C 1992
.pdfSpecial Pointers and Their Use
return(0);
}
void PullDown(
char * szMenu[],
int |
nColumn, |
void (__cdecl *pFunctions[])(void)) |
|
{ |
|
int |
i; |
int |
nMenuItem = -1; |
char |
chEntered; |
for (i = 0; szMenu[i]; i++)
{
printf(MOVE_CURSOR, i + 1, nColumn); printf(szMenu[i]);
}
while (nMenuItem < 0)
{
chEntered = (char)getch();
if (chEntered == '\0' || chEntered == '\xE0')
{// PC Extended character (function key etc.) chEntered = (char)getch();
}
chEntered = (char)_toupper((int)chEntered);
/* find the correct menu item index */
if (isalnum((int)chEntered))
{
for (i = 0; szMenu[i]; i++)
{
C C C
C4C C
C C C
continues
125
Part I • Honing Your C Skills
Listing 4.4. continued
if (strchr(szMenu[i], chEntered))
{
nMenuItem = i; break;
}
}
}
|
if (nMenuItem >= 0) |
|
{ |
|
pFunctions[nMenuItem](); |
|
} |
|
} |
} |
|
void |
DoFilesNew() |
{
printf(MOVE_CURSOR, 20, 10);
printf("Files, new");
printf(MOVE_CURSOR, 24, 10);
printf("Any key to continue");
(void)getch();
} |
|
void |
DoFilesOpen() |
{ |
|
/* |
Presents to the user a simple get a filename dialog box, |
* |
enabling character string to be |
entered. Basic editing supported. |
*/ |
|
|
int |
i; |
|
/* |
These hard-coded constants, for |
placement of dialog box, |
* |
normally would be passed. |
|
126
Special Pointers and Their Use
*/ |
|
|
int |
nColumn = 15; |
|
int |
nRow = 15; |
|
int |
nInputColumn = 2; |
|
int |
nInputRow = 4; |
|
char |
szFileName[132]; |
|
char |
*szFilesOpen[] = { |
|
"ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¿", |
||
"3 |
|
3", |
"3Enter the name of the file to open: |
3", |
|
"3 |
|
3", |
"3 |
........................................ |
3", |
"3 3", "АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ", NULL};
for (i = 0; szFilesOpen[i]; i++)
{
printf(MOVE_CURSOR, i + nRow, nColumn); printf(szFilesOpen[i]);
}
printf(MOVE_CURSOR, nInputRow + nRow, nInputColumn + nColumn);
scanf("%s", szFileName);
printf(MOVE_CURSOR, 24, 10);
printf("NAME: '%s' Any key to continue", szFileName);
(void)getch();
}
void DoFilesClose()
{
printf(MOVE_CURSOR, 20, 10);
printf("Files, close selected");
C C C
C4C C
C C C
continues
127
Part I • Honing Your C Skills
Listing 4.4. continued
printf(MOVE_CURSOR, 24, 10);
printf("Any key to continue");
(void)getch();
}
void DoFilesSave()
{
printf(MOVE_CURSOR, 20, 10);
printf("Files, save selected");
printf(MOVE_CURSOR, 24, 10);
printf("Any key to continue");
(void)getch();
}
void DoFilesSaveAs()
{
printf(MOVE_CURSOR, 20, 10);
printf("Files, save as selected");
printf(MOVE_CURSOR, 24, 10);
printf("Any key to continue");
(void)getch();
}
void DoFilesPrint()
{
printf(MOVE_CURSOR, 20, 10);
128
Special Pointers and Their Use |
C C C |
|
C4C |
|
C C C |
|
C |
printf("Files, print selected");
printf(MOVE_CURSOR, 24, 10);
printf("Any key to continue");
(void)getch();
}
void DoFilesEXit()
{
printf(MOVE_CURSOR, 20, 10);
printf("Files, exit selected");
exit(0);
}
void MenuBar()
{
/* This function is never called! */
}
MENU1.C is the most complex program this book has discussed. It shows several important features, including passing an array of function pointers and using screen control and—of course—menus.
One of the first things you do in MENU1 is define some string identifiers. These identifiers are used to format the menu items, position the cursor, and perform other screen-management functions:
/* ANSI.SYS screen control #define's below: */
#define BOLD |
"\x1B[1m" |
|
#define NORMAL |
"\x1B[0m" |
|
#define |
RED |
"\x1B[31m" |
#define |
BLACK |
"\x1B[30m" |
129
Part I • Honing Your C Skills
#define GREEN |
"\x1B[32m" |
|
#define |
CLEAR_SCREEN "\x1B[2J" |
|
#define |
CLEAR_EOL |
"\x1B[K" |
#define MOVE_CURSOR "\x1B[%d;%df"
Notice the identifier MOVE_CURSOR. Used with printf() and a set of integer parameters specifying cursor row and column, you can position the cursor using the following statement:
printf(MOVE_CURSOR, 10, 20);
The definition of the program’s top menu bar makes heavy use of string constant concatenation and ANSI screen control.
char szTopBar[] = {/* Must be 80 characters long MAX. */ CLEAR_SCREEN
BOLD"F"NORMAL"iles "
BOLD"E"NORMAL"dit "
BOLD"V"NORMAL"iew "
BOLD"P"NORMAL"roject "
BOLD"R"NORMAL"un "
BOLD"D"NORMAL"ebug " CLEAR_EOL
};
The maximum true length of the screen’s title bar is equal to the screen’s width. Counting these characters can be difficult; if you remove the ANSI screen-control identifiers and the string concatenation quotes, however, you can see the length of the menu bar more easily.
After the top menu-bar string definition, you define for the Files menu a pulldown that offers a number of common operations:
char |
|
|
*szFiles[] = { |
|
|
"ЪДДДДДДДДДДДДДДД¿", |
|
||
|
" |
|
3"BOLD"N"NORMAL"ew |
3", |
|
|
|||
|
" |
|
3"BOLD"O"NORMAL"pen |
3", |
|
" |
|
3"BOLD"C"NORMAL"lose |
3", |
|
" |
|
3"BOLD"S"NORMAL"ave |
3", |
|
" |
|
3save "BOLD"A"NORMAL"s |
3", |
|
|
|||
|
"ГДДДДДДДДДДДДДДД´", |
|
130
|
Special Pointers and Their Use |
C C C |
|
|
C4C |
|
|
C C C |
" "BOLD"P"NORMAL"rint |
", |
C |
|
||
"ÃДДДДДДДДДДДДДДД´", |
|
|
" e"BOLD"X"NORMAL"it |
", |
|
"ÀДДДДДДДДДДДДДДДÙ", |
|
|
NULL}; |
|
|
After you know what the Files pull-down menu will contain, you then build the function pointer array. You first define the functions, and then the array, and then initialize it with the functions that perform each task.
void |
DoFilesNew(); |
void |
DoFilesOpen(); |
void |
DoFilesClose(); |
void |
DoFilesSave(); |
void |
DoFilesSaveAs(); |
void |
DoFilesPrint(); |
void |
DoFilesEXit(); |
void |
(*FilesFunctions[])(void) = { |
|
MenuBar, |
|
DoFilesNew, |
|
DoFilesOpen, |
|
DoFilesClose, |
|
DoFilesSave, |
|
DoFilesSaveAs, |
|
MenuBar, |
|
DoFilesPrint, |
|
MenuBar, |
|
DoFilesEXit, |
|
MenuBar, |
|
NULL |
|
}; |
Notice that you have allowed the number of initializers to define how many elements are found in this array, and that you have set the final member to NULL so that you can test for the end of the array if necessary. Setting the last element of an array of pointers to NULL is a good idea because you don’t have to pass the length of the array to functions that use it.
Finally, just before you start the main() function, you define the function that controls the pull-down menus. This function’s prototype is
void PullDown(char **, int, void (__cdecl **)(void));
131
Part I • Honing Your C Skills
Notice how an array of pointers (usually written as *array[]) is described as an array of pointers to pointers (type **). This description is necessary because you don’t specify the name of the actual array in this prototype. The array of function pointers must be specified with both the return values and parameters. In this program, both are simply void.
The main program has just a large loop that reads the keyboard and processes the characters. This program uses getch() to get a character (without echoing it to the screen); because this program runs on a PC, you test (and process) special keypresses, such as the function keys. Other computers with different keyboards may require some other changes to this part of the program.
while (1)
{
printf(szTopBar);
chEntered = (char)getch();
if (chEntered == '\0' || chEntered == '\xE0')
{// PC Extended character (function key etc.) chEntered = (char)getch();
}
After a character is read in, it is converted to uppercase (so that it can be tested), and then you use a case statement to find which of the top bar menu items has been selected.
switch (_toupper((int)chEntered))
{
case 'F':
PullDown(szFiles, 1, FilesFunctions); break;
case 'E':
printf("Edit submenu called" CLEAR_EOL); break;
case 'V':
printf("View submenu called" CLEAR_EOL); break;
132
Special Pointers and Their Use |
C C C |
|
C4C |
|
C C C |
|
C |
case 'P':
printf("Project submenu called" CLEAR_EOL); break;
case 'R':
printf("Run submenu called" CLEAR_EOL); break;
case 'D':
printf("Debug submenu called" CLEAR_EOL); break;
default:
printf("Invalid key!" CLEAR_EOL); break;
}
When a top bar menu item is selected (Files in this example), the PullDown() function is called. This generic function is provided with the starting column for the pull-down menu (the starting row is always 2), an array of char pointers pointing to each menu item, and an array of function pointers pointing to the functions that will be called when a specific menu item is called. Except for Files, none of the top menu items are implemented.
PullDown() has the code to display the pull-down menu. A better program would save the screen at the location where the pull-down menu is displayed and restore it when the function returns; this simple program, however, doesn’t “clean up” after itself well:
for (i = 0; szMenu[i]; i++)
{
printf(MOVE_CURSOR, i + 2, nColumn); printf(szMenu[i]);
}
The menu items are printed, one to a line, until the end of the list (signified by the NULL pointer) is encountered. After the pull-down menu is displayed, you read the keyboard until a valid key is pressed, and then perform the requested action. Because this is a simple program, you again require the user to select a menu item before you let the user return to the main menu.
133
Part I • Honing Your C Skills
while (nMenuItem < 0)
{
chEntered = (char)getch();
if (chEntered == '\0' || chEntered == '\xE0')
{// PC Extended character (function key etc.) chEntered = (char)getch();
}
chEntered = (char)_toupper((int)chEntered);
/* find the correct menu item index */
if (isalnum((int)chEntered))
{
for (i = 0; szMenu[i]; i++)
{
if (strchr(szMenu[i], chEntered))
{
nMenuItem = i; break;
}
}
}
To check the keys pressed by the user, you get the character pressed, convert it to uppercase, and then scan each menu item for the key character. Because each menu item is allowed only one capitalized character (the desired character for this action), you can use strchr() to look at each of the menu lines. If no match is found, you wait for the user to press a new key; if a match is found, you call the appropriate function:
if (nMenuItem >= 0)
{
pFunctions[nMenuItem]();
}
}
Calling the correct function is as easy as indexing the array of function pointers.
MENU1 is a relatively crude program, yet it exceeds 300 lines of source code and doesn’t do anything. Remember my comments about reinventing the wheel at the beginning of the chapter? Now you can see why. I could write this program (and make
134