/* The original version of this program can be found at http://damb.dk */
#include <windows.h>
#include <stdlib.h>
/* This is an key element, it contains elements needed to handle one menu or submen item */
/* Each item has a text (title) and a associated function OR submenu */
/* If both function AND submenu are NULL we will jump one level up */
/* So our menu structure will be build as a tree of these */
typedef struct menu_item
{
const char *title;
struct menu_item const *submenu;
void(*function)(void);
}menu_item;
HANDLE Console;
HANDLE ConsoleIn;
#define NORM_ATTR (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
#define INVR_ATTR (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
#define EMPTY_LINE " "
void text_out(short attr, int x, int y, const char *text)
{
DWORD Dummy;
COORD Coord;
SetConsoleTextAttribute(Console, NORM_ATTR);
Coord.X = x + 5;
Coord.Y = y + 5;
SetConsoleCursorPosition(Console, Coord);
WriteConsole(Console, EMPTY_LINE, strlen(EMPTY_LINE), &Dummy, 0);
SetConsoleCursorPosition(Console, Coord);
SetConsoleTextAttribute(Console, attr);
WriteConsole(Console, text, strlen(text), &Dummy, 0);
}
void show_status(const char *msg)
{
text_out(NORM_ATTR, 0, 12, msg);
}
int get_key(void)
{
INPUT_RECORD Input;
DWORD NumRead;
do
{ /* Wait for key release */
ReadConsoleInput(ConsoleIn, &Input, 1, &NumRead);
}
while(Input.EventType != KEY_EVENT && Input.Event.KeyEvent.bKeyDown);
do
{ /* Read The Key */
ReadConsoleInput(ConsoleIn, &Input, 1, &NumRead);
}
while(Input.EventType != KEY_EVENT && !Input.Event.KeyEvent.bKeyDown);
return Input.Event.KeyEvent.wVirtualKeyCode;
}
#define DUMMY_FUNC(name) \
static void name(void) \
{ \
show_status(#name); \
}
DUMMY_FUNC(func_11)
DUMMY_FUNC(func_21)
DUMMY_FUNC(func_22)
DUMMY_FUNC(func_231)
DUMMY_FUNC(func_232)
DUMMY_FUNC(func_233)
void exit_func(void)
{
SetConsoleTextAttribute(Console, NORM_ATTR);
exit(0);
}
/* These are the menu's, will have to start from the buttom */
menu_item const pb_list_menu[] =
{
{"Ole", NULL, func_231},
{"Peter", NULL, func_232},
{"Poul", NULL, func_233},
{"Back", NULL, NULL},
{NULL, NULL, NULL}
};
menu_item const phonebook_menu[] =
{
{"AddEntry", NULL, func_11},
{"List", pb_list_menu, NULL},
{"Back", NULL, NULL},
{NULL, NULL, NULL}
};
menu_item const sub_menu_2[] =
{
{"New SMS", NULL, func_21},
{"List", NULL, func_22},
{"Back", NULL, NULL},
{NULL, NULL, NULL}
};
menu_item const main_menu[] =
{
{"PhoneBook", phonebook_menu, NULL},
{"SMS", sub_menu_2, NULL},
{"Exit", NULL, exit_func},
{NULL, NULL, NULL}
};
void exec(menu_item const * menu);
void clear_menu_window(void)
{
int i;
for(i = 0; i < 15; i++)
{
text_out(NORM_ATTR, 0, i, "");
}
}
int main(void)
{
int i;
DWORD Dummy;
COORD Coord;
Console = GetStdHandle(STD_OUTPUT_HANDLE);
ConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
SetConsoleMode(ConsoleIn, 0);
SetConsoleTextAttribute(Console, NORM_ATTR);
Coord.X = 1;
Coord.Y = 1;
SetConsoleCursorPosition(Console, Coord);
for(i = 0; i < 80; i++)
WriteConsole(Console, EMPTY_LINE, strlen(EMPTY_LINE), &Dummy, 0);
text_out(NORM_ATTR, -4, -4, "Simple menu demo Press UP/Down, Enter to select");
exec(main_menu);
SetConsoleTextAttribute(Console, NORM_ATTR);
return EXIT_SUCCESS;
}
/* This is the main menu executer, it will call itself to execute submenus */
void exec(menu_item const * menu)
{
int ch;
int i;
int state = 0;
clear_menu_window();
do
{
/* I'm lazy so I'll update the complete menu each time */
for(i = 0; menu[i].title; i++)
{
if(i == state)
text_out(INVR_ATTR, 0, i, menu[i].title);
else
text_out(NORM_ATTR, 0, i, menu[i].title);
}
ch = get_key();
switch(ch)
{
case VK_RETURN: /* Enter */
if(menu[state].function)
menu[state].function();
else if(menu[state].submenu)
{ /* It's a submenu */
exec(menu[state].submenu);
clear_menu_window();
}
else
{ /* It is a back menu item */
return;
}
break;
case VK_UP:
if(state != 0)
state--;
break;
case VK_DOWN:
if(menu[state + 1].title)
state++;
break;
}
}
while(1);
}