Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МП 1,2.doc
Скачиваний:
9
Добавлен:
05.11.2018
Размер:
2.77 Mб
Скачать

2.4. Определение выбора пользователя

Пользователь осуществляет свой выбор одним из двух способов. При первом способе он, нажимая клавиши '' и '', может переместить освещение на выбранную строку и нажать <ENTER>. Освещение производится с помощью вывода в инверсном режиме, черным по белому, с атрибутом 0x70. Второй способ – нажать ''горячую'' клавишу.

Для ввода управляющих кодов и символов применим функцию bioskey(). С помощью bioskey(1) ожидается нажатие клавиши, а с помощью bioskey(0) принимается код клавиши, состоящий из двух байт для управляющего кода и из одного – для символа.

Функцию is_in(), возвращающую номер выбранной ''горячей'' клавиши, разработаем после подпрограммы определения выбора пользователя, в которой она применяется:

int get_resp(int x, int y, int count, char *menu[], char *keys)

{

union inkey

{ // совмещены

char ch[2]; // массив

int i; // и число

} c;

int arrow_choice=0, // позиция в меню

key_choice; // горячая клавиша

write_string(x, y, menu[0], 0x70); // выделить первый выбор

for(;;) // бесконечный цикл

{

while(!bioskey(1)); // ждать нажатия

c.i = bioskey(0); // принять символ с клавиатуры

write_string (x, y+arrow_choice,

menu[arrow_choice], 7); // отмена выделения

if(c.ch[0]) // если c.ch[0]0, то обычная клавиша

{

key_choice = is_in(keys,tolower(c.ch[0]));

if(key_choice) return key_choice;

switch(c.ch[0])

{

case '\r': return arrow_choice; // нажата <ENTER>

case ' ' : arrow_choice++; break;

case 27 : return -1; // <ESCAPE>

break;

}

}

else // если c.ch[0]=0, то специальная клавиша

{

switch(c.ch[1])

{

case UP : arrow_choice--; // стрелка вверх

break; // UP=72

case DOWN: arrow_choice++; // стрелка вниз

break; // DOWN=80

}

}

if(arrow_choice==count) arrow_choice=0;

if(arrow_choice==-1) arrow_choice=count-1;

write_string(x, y+arrow_choice, menu[arrow_choice], 0x70);

// выделяет выбранную опцию

} // конец цикла

}

Здесь при нажатии стрелки вверх из положения опции 0 переменная arrow_choice становится равной (-1), и мы ей присваиваем номер последней опции count-1. Аналогично, если была нажата стрелка вниз из последнего положения, то эта переменная становится равной count , и мы ее переводим в положение 0. Тем самым осуществляется цикличность выбора опций меню. Напишем теперь подпрограмму выбора опции по «горячей» клавише:

int is_in (char *S, char C)

// возвращает номер в строке S символа C, если такого символа в S нет,

// то возвращает 0

{

register int i;

for (i=0; *S; i++)

if (*S++ == C) return i+1; // номер позиции +1

return 0; //нет такого символа

}

2.5. Подпрограмма исчезающего меню

Согласно пп.2.1, алгоритм создания исчезающего меню состоит из следующих шагов: сохранения области вывода меню, вывода меню и рамки, получения номера выбранной опции и восстановления экрана. Поскольку аргументами подпрограммы сохранения являются координаты углов прямоугольника области вывода меню, то перед вызовом этой подпрограммы нужно вычислить координаты этих углов. Если область вывода меню выходит за пределы экрана, то вывести сообщение о невозможности создания меню. Затем захватить область памяти для сохранения области вывода и перейти к выполнению следующих шагов. Последним, дополнительным, шагом будет вызов подпрограммы select(), выполняющей действия, соответствующие выбранному пункту меню.

Приходим к следующей подпрограмме:

int popup( char *menu[], char *keys, int count, int x, int y, int border)

{

register int i,len;

int endx, endy, choice;

unsigned char *p;

if((x>79)||(x<0)||(y>24)||(y<0))

{

printf("область меню выходит за пределы экрана");

return -2; // возвращает –2, если меню создать невозможно

}

len=0; // ширина меню

for (i=0; i<count; i++)

if(strlen(menu[i]) > len)

len = strlen(menu[i]);

endx = len+x+1;

endy = count+y+1;

if((endx > 79) || (endy > 24))

{

printf("область меню выходит за пределы экрана");

return -2;

}

p=(unsigned int *) malloc ((len+2)*(count+2)*2);

if(!p)

{

printf("нет памяти для области сохранения");

return -2;

}

save_video(x, endx, y, endy, p);

if (border) draw_border(x, y, endx, endy);

display_menu(menu, x+1, y+1, endx-1, endy-1, count);

choice = get_resp(x+1, y+1, count, menu, keys);

select(menu, choice);

return choice;

}

Пример. Предположим, что мы пишем программу, которая в режиме редактирования позволяет вводить текстовые данные. Требуется написать подпрограмму, которая изменяет атрибуты символов, выводимых на экран. Для того чтобы выполнить это задание, напишем подпрограмму исчезающего меню, состоящую из четырех опций: ''Цвет вывода'', ''Цвет фона'', ''Эхо'', ''Выход''. Если выбрана опция ''Цвет вывода'', то изменяется цвет вывода символов, если выбрана опция ''Цвет фона'', то изменяется цвет фона. При выборе ''Эхо'' главная программа возвращается в режим редактирования, а ''Выход'' приводит к выходу из главной программы. "Горячие клавиши'': 'т', 'ф', 'э', 'в'. Меню активизируется с помощью нажатия клавиши F2. Напишем главную программу, имитирующую режим редактирования, который осуществляется с помощью оператора switch. В случае нажатия стрелок влево, вправо, вверх и вниз, курсор перемещается на одну позицию в соответствующую сторону. Если нажат символ, то он выводится на экран. В случае нажатия F1 выводится сообщение о том, что для вызова меню нужно нажать F2.

Текст программы и подпрограмм:

#include "stdio.h"

#include "dos.h"

#include "stdlib.h"

#include "string.h"

#include "bios.h"

#include "ctype.h"

#include "conio.h"

#define BORDER 1

#define ESC 27 // Escape

#define ENTER 13

#define BKC 8

#define F1 59 // F1

#define F2 60 // F2

#define REV_VID 0x70

#define NORM_VID 7

#define LEFT 75 // стрелка влево

#define RIGHT 77 // стрелка вправо

#define UP 72 // стрелка вверх

#define DOWN 80 // стрелка вниз

void save_video(int, int, int, int, unsigned char*);

void restore_video(int, int, int, int, unsigned char*);

void display_menu(char**,int,int,int,int,int);

void draw_border(int, int, int, int);

void write_string(int, int, char*, int);

void write_char(int,int,char,int);

void select(char**, int);

int popup(char**,char*,int,int,int,int);

int get_resp(int,int,int,char**,char*);

int is_in(char*, char);

int video_mode();

char far *vid_mem;

int last_x, last_y;

int TEXT_ATTR=14, TEXT_COLOR=0, BK_COLOR=0;

char *menu[] =

{

" Цвет текста ",

" Цвет фона ",

" Эхо ",

" Выход "

};

void main(void)

{

char press;

int x,y;

clrscr(); // очистка экрана

int vmode = video_mode();

if((vmode!=2) && (vmode!=3) && (vmode!=7))

{

printf("должен быть 80 символьный текстовый режим");

exit(1);

}

/* присвоить соответствующий адрес видеопамяти */

if(vmode==7)

vid_mem=(char far *)0xB0000000; // монохромный режим

else

vid_mem=(char far *)0xB8000000; // цветной режим

window(2,2,79,24);

gotoxy(1,1);

while(1)

{

press = getch();

if(!press) press = getch(); // расширенная клавиатура

x=wherex(), y=wherey();

switch(press)

{

case LEFT : gotoxy(x-1,y);break;

case RIGHT: gotoxy(x+1,y);break;

case UP : gotoxy(x,y-1);break;

case DOWN : gotoxy(x,y+1);break;

case F1 : write_string(1,24,"F2 - меню, ESC - выход",TEXT_ATTR);

break;

case F2 : last_x=x;last_y=y;

popup(menu,"тфэв",4,60,0,BORDER); break;

case ENTER: cprintf("\n\r"); break;

case BKC : if (x==1) gotoxy(78,y-1);

else gotoxy(x-1, y);

write_char(wherex(),wherey(),' ',TEXT_ATTR); break;

case ESC : exit(1); break;

default : write_char(x,y, press,TEXT_ATTR);

if(x==78) gotoxy(1,y+1);

else gotoxy(x+1,y); break;

}

}

}

/* вывести исчезающее меню и вернуть выбор

возвращает -2, если меню не может быть создано

возвращает -1, если пользователь нажал клавишу ESC

в остальных случаях она возвращает номер выбранного

режима, начиная с 0 */

int popup( char *menu[], /* текст меню */

char *keys, /* горячие клавиши */

int count, /* число режимов */

int x, int y, /* координаты верхнего левого угла */

int border) /* если 0 то без рамки */

{

register int i,len;

int endx, endy, choice;

unsigned char *p;

if((x>79)||(x<0)||(y>24)||(y<0))

{

printf(" выход за пределы экрана");

return -2;

}

/* вычисление размеров меню*/

len=0;

for(i=0;i<count;i++)

if(strlen(menu[i]) > len)

len = strlen(menu[i]);

endx=len+x+1;

endy=count+y+1;

if((endx>79) || (endy>24))

{

printf(" выход за пределы экрана ");

return -2;

}

/* размещение памяти для видеобуфера */

p=(unsigned char *)malloc((len+2)*(count+2)*2);

if(!p) exit(1); /* Вы можете здесь сами обработать ошибку */

/* сохранение части экрана */

save_video(x, endx, y, endy, p);

/* рисуем рамку, если необходимо */

if(border) draw_border(x,y,endx,endy);

/* высвечивание меню на своем месте */

display_menu(menu,x+1,y+1,endx-1, endy-1, count);

/* ввести выбор пользователя */

choice=get_resp(x+1,y+1,count,menu,keys);

select(menu, choice);

/* восстановление части экрана */

restore_video(x, endx, y, endy, p);

/* освобождаем память*/

free(p);

return choice;

}

/* высвечивание меню на своем месте */

void display_menu(char* menu[], int startx, int starty, int endx, int endy,

int count)

{

register int i, j;

// закраска области меню

for(i=starty; i<=endy;i++)

for(j=startx; j<=endx; j++)

write_char(j, i , ' ', NORM_VID);

for(i=0;i<count;i++,starty++)

write_string(startx, starty, menu[i], NORM_VID);

}

/* рисование рамки меню */

void draw_border(int startx, int starty, int endx, int endy)

{

register int i;

write_char(startx, starty, 218, NORM_VID);

write_char(endx, starty, 191, NORM_VID);

write_char(startx, endy, 192, NORM_VID);

write_char(endx, endy, 217, NORM_VID);

for(i=startx+1; i<endx; i++)

{

write_char(i, starty, 196, NORM_VID);

write_char(i, endy, 196, NORM_VID);

}

for(i=starty+1;i<endy;i++)

{

write_char(startx, i,179, NORM_VID);

write_char(endx, i,179, NORM_VID);

}

}

/* ввести выбор пользователя */

int get_resp(int x, int y, int count, char* menu[], char* keys)

{

union inkey

{

char ch[2];

int i;

} c;

int arrow_choice=0, key_choice;

/* осветить первый выбор */

write_string(x, y, menu[0], REV_VID);

for(;;)

{

while(!bioskey(1)); // ждать нажатия

c.i=bioskey(0);

/* вернуть выбор в нормальный режим */

write_string(x, y+arrow_choice, menu[arrow_choice], NORM_VID);

if(c.ch[0]) // обычная клавиша

{

// горячая клавиша?

key_choice = is_in(keys,tolower(c.ch[0]));

if(key_choice) return key_choice;

switch(c.ch[0])

{

case '\r' : return arrow_choice;

case ' ' : arrow_choice++; break;

case ESC : return -1; // выйти

}

}

else // специальная клавиша

{

switch(c.ch[1])

{

case UP : arrow_choice--; // стрелка вниз

break;

case DOWN: arrow_choice++; // стрелка вверх

break;

}

}

/* осуществить цикличность меню */

if(arrow_choice==count) arrow_choice=0;

if(arrow_choice<0) arrow_choice=count-1;

/* подсветить выбранную опцию */

write_string(x, y+arrow_choice, menu[arrow_choice], REV_VID);

}

}

/* вывод строки с определенным атрибутом */

void write_string(int x, int y, char* p, int attrib)

{

register int i;

char far *v; // адрес символа

v = vid_mem; // в v помещаем адрес начала видеопамяти

v += y*160+x*2; // вычисляем адрес начала строки в видеопамяти

for(i=y;*p;i++)

{

*v++ = *p++; // записываем в видеопамять символ

*v++ = attrib; // записываем в видеопамять атрибут

}

}

/* запись символа с определенным атрибутом */

void write_char(int x, int y, char ch, int attrib)

{

char far *v; // адрес символа

v = vid_mem; // в v помещаем адрес начала видеопамяти

v += y*160+x*2; // вычисляем адрес символа в видеопамяти

*v++ = ch; // записываем в видеопамять символ

*v = attrib; // записываем в видеопамять атрибут

}

/* сохранение части экрана с использованием

прямого доступа к видеопамяти */

void save_video(int startx, int endx, int starty, int endy,

unsigned char *buf_ptr)

{

register int i, j;

char far *v, far *t;

v=vid_mem;

for(i=starty;i<=endy;i++)

{

for(j=startx; j<=endx; j++)

{

t = v+i*160+j*2; // вычисляем адрес

*buf_ptr++ = *t++; // записываем в буфер символ

*buf_ptr++ = *t; // записываем в буфер атрибут

}

}

}

/* восстановление части экрана с использованием

прямого доступа к видеопамяти */

void restore_video(int startx, int endx, int starty, int endy,

unsigned char *buf_ptr)

{

register int i,j;

char far *v,far *t; // временные переменные

v = vid_mem; // адрес начала видеопамяти

t = v;

for(i=starty; i<=endy; i++)

{

for(j=startx; j<=endx; j++)

{

v = t;

v += i*160+j*2; // вычисление адреса

*v++ = *buf_ptr++; // восстановление из буфера символа

*v = *buf_ptr++; // восстановление из буфера атрибута

}

}

}

/* запрос текущего видеорежима */

int video_mode()

{

union REGS r;

r.h.ah = 15; /* получить режим */

return int86(0x10,&r,&r) & 255;

}

/* проверка принадлежности символа с строке s */

int is_in(char* s, char c)

{

register int i;

for(i=0; *s; i++)

if(*s++ == c) return i;

return 0;

}

/* анализ выбора пункта меню */

void select(char* menu[], int pos)

{

// здесь должна быть Ваша обработка событий меню

switch(pos)

{

case 0: // увеличиваем индекс цвета текста

TEXT_COLOR++;

if (TEXT_COLOR<16) TEXT_ATTR=BK_COLOR*16+TEXT_COLOR;

else {TEXT_COLOR=0; TEXT_ATTR=BK_COLOR*16+TEXT_COLOR;} break;

case 1: // увеличиваем индекс цвета фона

BK_COLOR++;

if(BK_COLOR<16) TEXT_ATTR = BK_COLOR*16+TEXT_COLOR;

else {BK_COLOR=0; TEXT_ATTR = BK_COLOR*16+TEXT_COLOR;} break;

case 2: return;

case 3: exit(1);

}

}

Загрузим программу, введем символы и нажмем клавишу F2. Меню будет выведено в правой верхней части экрана:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]