// #include <stdlib.h>  /* exit() only! */
#include <textmenu.h>
#include <function.h>
#define SAVE_BUF_LEN 8000
 // Длина буфера для сохранения экрана
FUN Ext_Function;
#define between(a,x,b) (((x>=a)&&(x<=b))?YES:NO)
MODE WIN_X1=0,WIN_Y1=0,WIN_X2=24,WIN_Y2=79;
MODE FILLER=' ',
     ATTR=0x07,
     SHADOW=YES,
     EXPLODE=YES;

MODE DAY,MON; int YEAR; //Сегодняшняя дата

MODE Mouse=0x00; //Наличие "мыши"
MODE MouseX,MouseY;
MODE buttons;     // байт состояния/индикации кнопок
static unsigned ms_mask[32] =  { // AND-маска (маска экpана)
 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
 // XOR-маска (маска куpсоpа)
 0x0000, 0x0000, 0x0000, 0x0800, 0x0C00, 0x0E00, 0x0F00, 0x0F80,
 0x0FC0, 0x0FE0, 0x0F00, 0x0D80, 0x0980, 0x00C0, 0x00C0, 0x0000
};

MODE buffer[SAVE_BUF_LEN];
static int bufpos;
static KEY Buf[ MAX_BUF ];  //Внутренний буфер клавиатуры
static KEY Start = 0,       //Указатели в буфере
	   End = 0;
static MODE VideoPage; //Видеостраница
static MODE SaveCursorX[8],SaveCursorY[8];
MODE HorRow,HorItems,Columns[12],HorKeys[12]; // Для обмена с PullDown
					      // ч/з "мышь"

MODE far *vid_mem=(unsigned char far *)0xb8000000L,
     far *V;


void CURSOR_ON(void) {
 if (Mouse) {
  asm mov ax,1;
  asm int 033h;
 } return;
}

void CURSOR_OFF(void) {
 if (Mouse) {
  asm mov ax,2;
  asm int 033h;
 } return;
}

void set_active_page (MODE page) {
 vid_mem=(unsigned char far *)0xb8000000L+4096*page;
}

void set_visual_page (MODE page) {
 CURSOR_OFF();
 asm mov ah,5;
 asm mov al,page;
 asm int 10h;
 CURSOR_ON();
 VideoPage=page;
}

MODE set_attr (char col, char bk) {
 MODE ATTR=0x00;
 ATTR|=col;
 ATTR|=(bk<<4);
 return (ATTR);
}

void write_char (char x, char y, char ch) {
 V=vid_mem + (x<<7) + (x<<5) + (y<<1); // Сдвиг быстрее, чем умножение !
 *V++ = ch; // запись символа
 *V = ATTR; // запись атрибута
}

void write_string (char x, char y, char *str, char attr) {
 char i;
 CURSOR_OFF();
 V=vid_mem + (x<<7) + (x<<5) + (y<<1);
 for (i=0;  *(str+i); i++) {
  *V++ = *(str+i); // запись символа
  *V++ =   attr;     // запись атрибута
 }
 CURSOR_ON();
}

void clear (char x1,char y1,char x2,char y2) {
 char i,j;
 x2++; y2++;
 CURSOR_OFF();
 for (i=x1; i<x2; i++) {
  V = vid_mem + (i<<7) + (i<<5) + (y1<<1);
  for (j=y1; j<y2; j++) {
   *V++ = FILLER; // запись символа
   *V++ =   ATTR; // запись атрибута
  }
 }
 CURSOR_ON(); return;
}

void center_string  (char x, char y1, char y2, char *str) {
 char i,y;
 y=(y1+y2-strlen_(str))>>1; CURSOR_OFF();
 for (i=0;  *(str+i); i++) write_char ( x, y+i, *(str+i));
 CURSOR_ON(); return;
}

/* char read_char (char x, char y, char *attr) {
 char ch;
 V = vid_mem + (x<<7) + (x<<5) + (y<<1);
 ch = *V++;  // символ
 *attr = *V; // атрибут
 return (ch);
}  */

void change_attr (char x1, char y1, char x2, char y2, char bkcolor) {
 MODE i,j,attr;
 x2++; y2++;
 bkcolor= (bkcolor&0x07)<<4; CURSOR_OFF();
 for (i=x1; i<x2; i++) {
  V = vid_mem + (i<<7) + (i<<5) + (y1<<1);
  for (j=y1; j<y2; j++) {
   V++;
   attr=((*V)&0x8f)|bkcolor;
   *V++ = attr; // запись атрибута
  }
 } CURSOR_ON();
}

void save (char x1,char y1,char x2,char y2) {
 char i,j,ch,attr;
 x2++; y2++;
 CURSOR_OFF();
 for (i=x1; i<x2; i++) {
  V = vid_mem + (i<<7) + (i<<5) + (y1<<1);
  for (j=y1; j<y2; j++) {
   ch =   *V++;  /* символ */
   attr = *V++;  /* атрибут */
   buffer[bufpos++]=ch;
   buffer[bufpos++]=attr;
  }
 }
 CURSOR_ON(); return;
}

void restore (char x1,char y1,char x2,char y2) {
 MODE ch,i,j,attr;
 int pos;
 CURSOR_OFF();
 bufpos-=(((x2-x1+1)*(y2-y1+1))<<1);
 pos=bufpos; x2++; y2++;
 for (i=x1; i<x2; i++) {
  V = vid_mem + (i<<7) + (i<<5) + (y1<<1);
  for (j=y1; j<y2; j++) {
   ch=buffer[pos++];
   attr=buffer[pos++];
   *V++ = ch; // запись символа
   *V++ = attr; // запись атрибута
  }
 }
 CURSOR_ON(); return;
}

void box (char x1, char y1, char x2, char y2, char sort) {
// Отрисовка рамки:
//  012 ┌─┐ ╔═╗ ┌─╖ ╒═╕ ╓─╖ ▄▄▄ ▄▄▄
//  3 4 │1│ ║2║ │3║ │4│ ║5║ ▌6▐ █7█
//  567 └─┘ ╚═╝ ╘═╝ ╘═╛ ╙─╜ ▀▀▀ ▀▀▀
 unsigned char border[8][8]={
  {' ',' ',' ',' ',' ',' ',' ',' '},
  {'┌','─','┐','│','│','└','─','┘'},
  {'╔','═','╗','║','║','╚','═','╝'},
  {'┌','─','╖','│','║','╘','═','╝'},
  {'╒','═','╕','│','│','╘','═','╛'},
  {'╓','─','╖','║','║','╙','─','╜'},
  {'▄','▄','▄','▌','▐','▀','▀','▀'},
  {'▄','▄','▄','█','█','▀','▀','▀'}
 };
 char i;
 if (!sort) for (i=0; i<8; i++) border[0][i]=FILLER; // Задан заполнитель
 CURSOR_OFF();
 for(i=x1+1; i<x2; i++) { // Вниз
  write_char (i,y1,border[sort][3]);
  write_char (i,y2,border[sort][4]);
 }
 for (i=y1+1; i<y2; i++) { // Вправо
  write_char (x1,i,border[sort][1]);
  write_char (x2,i,border[sort][6]);
 }
 write_char( x1, y1, border[sort][0]);
 write_char( x1, y2, border[sort][2]);
 write_char( x2, y1, border[sort][5]);
 write_char( x2, y2, border[sort][7]);
 CURSOR_ON();
 return;
}

void window (char x1,char y1,char x2,char y2) {
 char j,cx1,cy1,cx2,cy2;
 if (EXPLODE) {
  cy1=(y2+y1)>>1;
  cy2=cy1+1;
  cx1=(x2+x1)>>1;
  cx2=cx1+1;
  j=0x0f;
  do {
   box (cx1,cy1,cx2,cy2,0x02);
   FILLER=' ';
   box (cx1,cy1,cx2,cy2,0x00);
   if (cx1>x1) cx1--;
   else j&=0x0e;
   if (cx2<x2) cx2++;
   else j&=0x0d;
   if (cy1>y1) cy1--;
   else j&=0x0b;
   if (cy2<y2) cy2++;
   else j&=0x07;
  } while (j);
 }
 else { // normal
  FILLER=' ';
  clear (x1,y1,x2,y2);
 }
 box (x1,y1,x2,y2,0x02);
 if (SHADOW) {
  j=y2+2;
  if (j<80) change_attr (x1+1, y2+1, x2, y2+2, 0x08); //Ver
  j=x2+1;
  if (j<25) change_attr (x2+1,y1+2,x2+1,y2+2, 0x08); //Hor
 } return;
}

void Set_Window (MODE x1,MODE y1,MODE x2,MODE y2) {
 WIN_X1=x1; WIN_X2=x2;
 WIN_Y1=y1; WIN_Y2=y2;
}

char far  CHAR_TABL[128] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, //00H
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, //08H
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, //10H
0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, //18H
0x20,0x21,0x9D,0x2F,0xFD,0x3A,0x2E,0xED, //20H  !"# $%&'
0x3F,0x25,0x3B,0x2B,0xA1,0x2D,0xEE,0xF0, //28H ()*+ ,-./
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, //30H 0123 4567
0x38,0x39,0x86,0xA6,0x81,0x3D,0x9E,0xF1, //38H 89:; <=>?
0x22,0x94,0x88,0x91,0x82,0x93,0x80,0x8F, //40H @ABC DEFG
0x90,0x98,0x8E,0x8B,0x84,0x9C,0x92,0x99, //48H HIJK LMNO
0x87,0x89,0x8A,0x9B,0x85,0x83,0x8C,0x96, //50H PQRS TUVW
0x97,0x8D,0x9F,0xE5,0x5C,0xEA,0x2C,0x5F, //58H XYZ[ \]^_
0x29,0xE4,0xA8,0xE1,0xA2,0xE3,0xA0,0xAF, //60H `abc defg
0xE0,0xE8,0xAE,0xAB,0xA4,0xEC,0xE2,0xE9, //68H hijk lmno
0xA7,0xA9,0xAA,0xEB,0xA5,0xA3,0xAC,0xE6, //70H pqrs tuvw
0xE7,0xAD,0xEF,0x95,0x7C,0x9A,0x28,0x7F  //78H xyz{ |}~
};

KEY  Get_Key( void ) {
 KEY  sim;              //Символ, кторый будет содержать значение буфера
 MODE scan,ascii;
 int mod;
 if (Start != End) {     //Есть ли символ во внутреннем буфере
  sim = Buf[ End++ ];  //Есть, считать символ
  End %= MAX_BUF;    //Контроль выхода за пределы буфера
 }
 else  {
  asm MOV  AH,0x00;     //Считываем символ из буфера клавиатуры
  asm    INT  16H;
  sim=_AX; //bioskey (0);
  scan=(MODE)((sim&0xff00)>>8);  //_AH
  ascii=(MODE)(sim&0x00ff); //_AL
  if (ascii) {
   scan=0;
   asm MOV  AH,0x02;     //Считываем символ из буфера клавиатуры
   asm INT  16H; //mod=bioskey(2);
   mod=_AX;
   if (mod&0x0010) { //SCROLL LOCK ON
    ascii=CHAR_TABL[ascii];
   }
  }
  sim=(scan<<8)+ascii;
 }
 return (sim);
}

void Set_Key( KEY kol, ... ) {
  KEY  *ptr;
  ptr = &kol;           //Указываем на первый символ в строке парам.
  for(; kol != 0; kol-- )   {
    Buf[ Start++ ] = *++ptr;  //Последовательно записываем все символы
    Start %= (MAX_BUF);     //во внутренний буфер
  }
  return;
}

void Set_Insert_Key( KEY kol, ... ) {
  KEY  *ptr = &kol;           //Указываем на первый символ в строке парам.
  for(; kol != 0; kol-- )   {
    if( !(End)-- )        //Последовательно записываем все символы
      End = MAX_BUF-1;
    Buf[ End ] = *++ptr;   //во внутренний буфер
  }
  return;
}

void Clear_Buf( void ) {
  End = Start;
  asm{
      PUSH  ES            //Сохраняем доплнительный сегмент
      CLI                 //Запрещаем прерывания
      SUB   AX,AX
      MOV   ES,AX
      MOV   AX,ES:[41AH] //Устанавливаем указатели буфера на
      MOV   ES:[41CH],AX //одно и тоже значение
      STI                 //Разрешаем прерывания
      POP   ES            //Восстанавливаем дополнительный сегмент
     }
 return;
}

KEY Hit_Key (void) {
if (Start != End) return (1);
asm push es;
asm  mov ax,0;
asm  mov es,ax;
asm  mov ax,es:[41ah];
asm  mov cx,es:[41ch];
asm pop es;
 if (_AX==_CX) return (0);
 return (1);
}

/* KEY Pop_Key (MODE Pos) {
 //Возвращает символ, хранящийся в буфере клавиатуры со смещением
 //Pos от начала, если такого смещения нет, возвращает 0
 KEY Ret=0,BufKeys[MAX_BUF];
 MODE i,k;
 if (Pos>MAX_BUF-1) return 0;
 for (i=0; i<=Pos; i++) {
  asm mov ah,011h;
  asm int 16h;
  asm jz VOID_BUF;
  BufKeys[i]=_AX;
 }
VOID_BUF:
 for (k=0; k<i; k++) {
  Push_Key (BufKeys[i-1-k]);
 }
 return Ret;
 }

MODE Pressed_Key (KEY Key) {
 //Возвращает YES, если была нажата клавиша Key
 MODE Ret=0x00;
 asm push es;
  asm popf
  asm popa
 asm mov ax,0;
 asm mov es,ax;
 asm mov bx,es:[41ah]; //Начало буфера
 asm mov dx,es:[41ch]; //Конец буфера
 asm dec bx;
 asm dec bx;
 asm cmp dx,0003ch; //Указатель конца=Конец буфера-2?
 asm jne TEST_POS;
 asm cmp bx,0001eh; //Буфер переполнен?
 asm jne TEST_POS;
 asm jmp OUT;
TEST_POS:
 asm mov ax,es:[bx]; //Символ в AX
 asm cmp ax,Key;
 asm je FOUND;
 asm inc bx;
 asm inc bx;
 asm cmp bx,dx;
 asm jge OUT;
 asm cmp bx,0003ch; //Указатель =Конец буфера-2?
 asm jge OUT;
 asm jmp TEST_POS;
FOUND:
 Ret=0x01;
OUT:
 asm popa
 asm popf
 asm pop es;
 return Ret;
}
MODE Pressed_Key (KEY Key) {
 MODE Ret=0x00;
 asm push es;
 asm push ax;
 asm push bx;
 asm mov bx,es:[41ch]; //Конец буфера
 asm sub bx,2;
 asm mov ax,es:[bx];
// asm mov ah,011h;
// asm int 16h;
// asm jz OUT;
// asm cmp ax,Key;
 asm jnz OUT;
 Ret=0x01;
OUT:
 asm push bx;
 asm push ax;
 asm push es;
 return Ret;
}
*/
/* MODE Push_Key (KEY c) {
 //Помещает символ в физический буфер клаиатуры; только для BIOS AT!
 //Возвращает YES при успехе
 asm mov cx,c;
 asm mov ah,005h;
 asm int 16h;
 asm cmp al,0;
 asm je OK;
 return NO;
OK:
 return YES;
} */

//////////////////////////////////////////////////////////////
//                    Часть для мыши
//////////////////////////////////////////////////////////////

void ms_state(void) {
 asm mov ax,3;
 asm int 033h;
 asm mov buttons,bl;
 asm shr cx,3;
 asm shr dx,3;
 asm mov MouseY,cl;
 asm mov MouseX,dl;
 return;
}

int ms_release(void) {
 asm mov ax,6;
 asm int 033h;
 asm shr cx,3;
 asm shr dx,3;
 asm mov MouseY,cl;
 asm mov MouseX,dl;
 return (_BX);
}

//  Пеpемещает куpсоp "мыши" в заданную позицию экpана.
void ms_move(MODE MouseX, MODE MouseY) {
 if (Mouse) {
  asm mov ch,0;
  asm mov cl,MouseY;
  asm shl cx,3;
  asm mov dh,0;
  asm mov dl,MouseX;
  asm shl dx,3;
  asm mov ax,4;
  asm int 033h;
 } return;
}

#define PROGRAM_MOUSE_CURSOR 0
#define HARD_MOUSE_CURSOR    1

void set_text_cursor(int type, int start, int end) {
 asm mov bx,type;
 asm mov cx,start;
 asm mov dx,end;
 asm mov ax,0ah;
 asm int 033h;
 return;
}

MODE Init_Mouse (void) {
 asm mov ax,0;
 asm int 033h;
 if(_AX==0) return NO;
 else {
  set_text_cursor(PROGRAM_MOUSE_CURSOR,0xffff,0x7700); //Обычный курсор
  asm mov ax,1;
  asm int 033h;
  return YES;
 }
}

MODE AnyClick (void) { // Любой щелчок мышью или нажатие клавиши
 Clear_Buf();
 while (!Hit_Key()) {
  if (Mouse) {
   ms_state();
   if (buttons) {
    buttons--;
    while (!ms_release());
    return 0x01;
   }
  }
 }
 Clear_Buf(); return 0x00;
}

MODE Mouse_In_Field (MODE X1,MODE Y1, MODE Xsize, MODE Ysize,
 MODE Xnum, MODE Ynum, MODE Xdop, MODE Ydop, MODE *I, MODE *J) {
 // X1,Y1 -  координаты левого верхнего угла
 // Xsize,Ysize - размеры выделяемого блока
 // Xnum,Ynum - количество блоков по горизонтали и вертикали
 // Xdop,Ydop - пустое место между блоками по горизонтали и вертикали
 // *I,*J - клеточные координаты выбранного блока
 // Возвращает 1, если присвоены X,Y, иначе они максимальны
 int X2,Y2,XX,YY,cx,cy;
 X2=X1+Xsize*Xnum+Xdop*(Xnum-1);
 Y2=Y1+Ysize*Ynum+Ydop*(Ynum-1);
 XX=Xsize+Xdop; YY=Ysize+Ydop;
 *I=Xnum; *J=Ynum;
 if (MouseX>=X1 && MouseX<=X2 &&
     MouseY>=Y1 && MouseY<=Y2) {
  cx=(MouseX-X1)/XX;
  if (MouseX<=X1+XX*cx+Xsize) *I=cx;
  cy=(MouseY-Y1)/YY;
  if (MouseY<=Y1+YY*cy+Ysize) *J=cy;
  if (*I<Xnum && *J<Ynum) {
   buttons=0x00; while (ms_release()<1); // до отпускания кнопки мыши
   return 0x01;
  }
 }
 return 0x00;
}

MODE Fun_Bar_Keys (MODE Num_Keys,KEY *Bar_Keys,MODE *Pos_Keys) {
 MODE i;
 if (MouseX==24) {
  for (i=0; i<Num_Keys; i++)
  if (MouseY>=Pos_Keys[i] && MouseY<Pos_Keys[i+1]) {
   Set_Key (1,Bar_Keys[i]);
   buttons=0x00; while (ms_release()<1); // до отпускания кнопки мыши
   return (0x01);
  }
 } return (0x00);
}

void Show_Button (BUTTON *b) {
 MODE i,c1,c2,y1,y2;
 if (b->zam) { c1='▀'; c2='▄'; y1=b->y1 +1; y2=b->y2+1; }
 else { c1=c2=' '; y1=b->y1 ; y2=b->y2; }
 FILLER=' '; ATTR=set_attr (b->color1, b->bkcolor);
 clear (b->x, b->y1, b->x, b->y2);
 Print (b->x, y1-1, b->color1, b->color2, b->str);
 ATTR=set_attr(LIGHTGRAY,b->bodcolor);
 CURSOR_OFF();
 for (i=y1; i<=y2; i++) write_char (b->x +1, i, c1);
 write_char (b->x , y2, c2);
 CURSOR_ON();
}

MODE Push_Button (BUTTON *b) {
 if (MouseX== b->x && MouseY>=b->y1 && MouseY<=b->y2) {
  b->zam=NO;  (b->y1)++; (b->y2)++;
  Show_Button (b);
  buttons=0x00; while (!ms_release());
  b->zam=YES; (b->y1)--; (b->y2)--;
  Show_Button (b);
  Set_Key (1, b->key);
  return YES;
 }
 return NO;
}

MODE Print (MODE x,MODE y,MODE Color1,MODE Color2,MODE *Str) {
 //Возвращает YES, если строка влезла в окно, иначе NO
 char c,cx,cy,Color=Color1,Outy=NO,Ret=NO,Attr;
 int i;
 cx=WIN_X1+x; cy=WIN_Y1+y;
 Attr=ATTR&0xf0; Attr|=Color;
 CURSOR_OFF();
 for (i=0; ;i++) {
  c=*(Str+i);
  switch (c) {
   case '$':
    if (*(Str+i+1)=='$') { i++; break; }
    Color=(Color==Color1?Color2:Color1);
    Attr&=0xf0; Attr|=Color;
   continue;
   case '\r': continue;
   case '\n':
    if (cx<WIN_X2) cx++;
    else goto RET;
    cy=WIN_Y1+y; Outy=NO;
   continue;
   case '\0':
    Ret=YES;
RET:
    CURSOR_ON(); return Ret;
   default: break;
  }
  if (!Outy) {
   V=vid_mem + (cx<<7) + (cx<<5) + (cy<<1);
   *V++ = c; // запись символа
   *V = Attr; // запись атрибута
  }
  if (cy<WIN_Y2) cy++;
  else Outy=YES;
 }
}

void Fun_Bar( MODE *str ) {
 MODE SaveByte1=ATTR;
 ATTR=0x70;
 FILLER=' ';
 clear (24,0,24,79); //Очищаем строку
 Print( 24, 0, LIGHTBLUE, LIGHTRED, str ); //Выводим строку
 ATTR=SaveByte1; return;
}

MODE Find_Key( MODE *str, MODE sim ) {
 MODE i = 0; //Смещение в строке
 //Цикл до совпадения или конца строки
 while( ( *str ) && ( *str++ != sim) )
  i++;
 if ( *str )    //Был ли конец строки
  i >>= 2;    //Нет, тогда MOD 4
 else i = NO_SIM;    //Да, возвращаем NO_SIM
 return( i );
}

void Clr_Scr (void) {
 FILLER='░';
 ATTR=set_attr (YELLOW,BLUE);
 clear (1,0,23,79);
}

void Nop (void) {};

char textmode (void) {
 // Устанавливает режим 3, 80*25, 16 цветов
 // Возвращает 0 в случае ошибки
 asm mov ax,0003h;
 asm int 10h;
 asm mov ah,00fh;
 asm int 10h;
 asm cmp al,003h;
 asm jnz ModeError;
 asm mov al,1;
 asm jmp Exit;
ModeError:
 asm mov al,0;
Exit:
 return (_AL);
}

void goto_xy(char xpos,char ypos) {
 asm mov ah,0x02 ;
 asm mov dl,ypos ; //Столбец
 asm mov dh,xpos ; //Строка
 asm mov bh,VideoPage ; //Страница
 asm int 10h;
}

void size_cursor (MODE swit) {
 MODE hi,low;
 if (swit) { hi=0x01; low=0x0d; }
 else { hi=0x0c; low=0x0d; }
 asm {
  MOV	AH,0x01
  MOV	CH,hi
  MOV	CL,low
  INT	10H
 }
}

void show_cursor (MODE swit) {
 // 0 - выключить курсор
 // 1 - включить на сохраненной позиции (по умолчанию 0,0)
 if (swit) goto_xy (SaveCursorX[VideoPage],SaveCursorY[VideoPage]);
 else {
  asm {  // Save cursor
   mov ah,3;
   mov bh,VideoPage;
   int 10h;
  }
  SaveCursorX[VideoPage]=_DH;
  SaveCursorY[VideoPage]=_DL;
  goto_xy (25,0);
 }
}

MODE Popup (MENU *Menu, MENUITEM *Item) {
 // Падающее меню; возвращает номер выбора (с 0) или NO_SIM по нажатию ESC
 char *Bar=
  " $ESC$ Отказ  $F1$ Помощь  $DOWN$ Вниз  $UP$ Вверх  $ENTER$ Выбор";
 KEY  Bar_Keys[]={ESC,F1,DOWN,UP,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,33,43,54},
      cursor=Menu->Setup,rev,i,j,x1,y1,x2,y2,kol;
 x1=Menu->x1; x2=Menu->x2;
 y1=Menu->y1; y2=Menu->y2;
 kol=Menu->kol - 0x01;
 if (Menu->Save) save (x1,y1,x2,y2);
 ATTR=set_attr (Menu->color1,Menu->bkcolor);
 rev=(15-Menu->bkcolor);

 FILLER=' ';
 clear (x1,y1,x2,y2);
 box (x1,y1,x2,y2,0x02);
 x1++; y1++;
 for (i=0; i<=kol; i++) {
  if ((Item+i)->zam) Print (x1+i, y1, Menu->color1, Menu->color2, (Item+i)->str);
  else Print (x1+i, y1, rev, rev, (Item+i)->str);
 }
 Fun_Bar (Bar);
 while (1) {
  change_attr (x1+cursor, y1, x1+cursor, y2-1, rev);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (5,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (Mouse_In_Field (x1,y1, 1,y2-y1, kol+1,1, 0, 0, &i, &j)) {
     change_attr (x1+cursor, y1, x1+cursor, y2-1, Menu->bkcolor);
     cursor=i; sim=ENTER;
     change_attr (x1+cursor, y1, x1+cursor, y2-1, rev);
     goto SWITCHER;
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
SWITCHER:
  change_attr (x1+cursor, y1, x1+cursor, y2-1, Menu->bkcolor);
  switch (sim) {
   case ESC: cursor=NO_SIM;  goto OUT;
   case F1:
    if (Menu->Index==NO_HELP) Help (ABOUT_POPUP);
    else Help (Menu->Index+cursor);
    Fun_Bar (Bar);
   break;
   case ENTER:
    if ((Item+cursor)->zam) {
     Menu->Setup=cursor;
     (Item+cursor)->sf();
     Fun_Bar (Bar);
    }
   break;
   case UP: case S_TAB:
    if (cursor>0x00) cursor--;
    else cursor=kol;
   break;
   case DOWN: case TAB:
    if (cursor<kol) cursor++;
    else cursor=0x00;
   break;
   case PAGE_UP: cursor=0x00; break;
   case PAGE_DO: cursor=kol; break;
   default:
    i=Find_Key(Menu->keys,(char)sim);
    if (i!=NO_SIM) {
     cursor=i;
     Set_Key (1,ENTER);
    }
   break;
  }
 }
OUT:
 x1--; y1--;
 if (Menu->Save) restore (x1,y1,x2,y2);
 return (cursor);
}


MODE HorMenu ( MENU *Menu, MENUITEM *Item ) {
 // Горизонтальное меню; возвращает последний сделанный выбор
 MODE cursor = Menu->Setup;   //Указатель на активный пункт меню
 KEY sim;         //Символ принимаемый от пользователя
 char *Bar=
 "$F1$ Справка  $ENTER$ Выбор  $HOME$ Начало  $END$ Конец  $LEFT$ Влево  $RIGHT$ Вправо";
 KEY Bar_Keys[6]={F1,ENTER,HOME,END,LEFT,RIGHT};
 MODE Pos_Keys[7]={1,13,26,39,48,62,75};
 MODE i,j,y1,y2,x,rev,kol;

 y1=Menu->y1; y2=Menu->y2;
 HorRow=x=Menu->x1;  kol=Menu->kol - 0x01;
 HorItems=kol+1;
 if (Menu->Save) save (x,y1,x,y2);

 rev=BLACK;
 ATTR=set_attr (Menu->color1, Menu->bkcolor);
 FILLER=' ';
 show_cursor (NO);
 clear (x,y1,x,y2);

 Columns[0]=y1;
 Print (x, y1, Menu->color1, Menu->color2, (Item+0)->str);
 for (i=1; i<=kol; i++) {
  Columns[i]= Columns[i-1] + strlen__ ((Item+i-1)->str,&j) + 2;
  Print (x,Columns[i], Menu->color1, Menu->color2, (Item+i)->str);
  HorKeys[i-1]=Menu->keys[(i-1)<<2]; // Запоминаем для PullDown
 }
 HorKeys[i-1]=Menu->keys[(i-1)<<2]; // Запоминаем для PullDown
 Columns[kol+1]= Columns[kol] + strlen__ ((Item+i-1)->str,&j) + 2;
 Fun_Bar (Bar);
 while( 1 )  { // Цикл до тех пор пока пользователь не нажмет ESC
  change_attr (x, Columns[cursor], x, Columns[cursor+1]-2, rev);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (6,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (MouseX==x) {
     for (i=0; i<=kol; i++)
     if (MouseY >= Columns[i] && MouseY<= Columns[i+1]-2) {
      buttons=0x00; while (ms_release()<1); // до отпускания кнопки мыши
      Set_Key (1, Menu->keys[i<<2]);
      goto KEYBOARD;
     }
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
  change_attr (x, Columns[cursor], x, Columns[cursor+1]-2, Menu->bkcolor);
  switch (sim) {
   case ESC: goto OUT;
   case F1:
    if (Menu->Index==NO_HELP) Help (ABOUT_HORMENU);
    else Help (Menu->Index + cursor);
    Fun_Bar (Bar);
   break;
   case START:
    change_attr (x, Columns[cursor], x, Columns[cursor+1]-2, rev);
    Ext_Function();
    change_attr (x, Columns[cursor], x, Columns[cursor+1]-2, Menu->bkcolor);
    Fun_Bar (Bar);
   break;
   case DOWN: case ENTER:
    if ((Item+cursor)->zam) {
     Menu->Setup=cursor;
     change_attr (x, Columns[cursor], x, Columns[cursor+1]-2, rev);
     (Item+cursor)->sf();
     change_attr (x, Columns[cursor], x, Columns[cursor+1]-2, Menu->bkcolor);
     Fun_Bar (Bar);
    }
   break;
   case LEFT: case S_TAB:
    if (cursor>0x00) cursor--;
    else cursor=kol;
   break;
   case RIGHT: case TAB:
    if (cursor<kol) cursor++;
    else cursor=0x00;
   break;
   case HOME: cursor=0x00; break;
   case END: cursor=kol; break;
   default:
    i=Find_Key(Menu->keys,(char)sim);
    if (i!=NO_SIM) {
     cursor=i;
     Set_Key (1,ENTER);
    }
   break;
  }
 }
OUT:
 if (Menu->Save) restore (x,y1,x,y2);
 show_cursor (YES);
 return (cursor);
}

static MODE LineType, BordType1, BordType2, Byte;
void HorizLine (MODE x,MODE y1,MODE y2,MODE Type) {
 //Расшифровка Type:
 //xxxx 1xxx - рисовать края
 //xxxx xx1x - тип 1 края
 //xxxx x1xx - тип линии
 //xxxx xxx1 - тип 2 края
 MODE Board1[]={'├','╞','╟','╠'}, Board2[]={'┤','╢','╡','╣'},
  Line[]={'─','═'};
 CURSOR_OFF();
 LineType=(Type&0x02)>>1;
 BordType1=(Type&0x06)>>1;
 BordType2=Type&0x03;
 if (Type&0x08) { //Рисовать края
  write_char (x,y1,Board1[BordType1]);
  write_char (x,y2,Board2[BordType2]);
  y1++; y2--;
 }
 for (Byte=y1; Byte<=y2; Byte++) write_char (x,Byte,Line[LineType]);
 CURSOR_ON();
}

void VertLine (MODE x1,MODE x2,MODE y,MODE Type) {
 MODE Board1[]={'┬','╥','╤','╦'}, Board2[]={'┴','╧','╨','╩'},
  Line[]={'│','║'};
 CURSOR_OFF();
 LineType=(Type&0x02)>>1;
 BordType1=(Type&0x06)>>1;
 BordType2=Type&0x03;
 if (Type&0x08) { //Рисовать края
  write_char (x1,y,Board1[BordType1]);
  write_char (x2,y,Board2[BordType2]);
  x1++; x2--;
 }
 for (Byte=x1; Byte<=x2; Byte++) write_char (Byte,y,Line[LineType]);
 CURSOR_ON();
}

MODE PullDown (MENU *Menu, MENUITEM *Item) {
 // Падающее меню первого уровня с переходом к соседним меню;
 // возвращает номер последнего выбора (с 0)
 char *Bar=
 "$ESC$ Отказ  $F1$ Справка  $DOWN$ Вниз  $UP$ Вверх  $LEFT$ Меню слева  $RIGHT$ Меню справа";
 KEY Bar_Keys[]={ESC,F1,DOWN,UP,LEFT,RIGHT},sim;
 MODE Pos_Keys[]={0,10,22,33,43,60,78};
 MODE cursor=Menu->Setup,rev,i,j,x1,y1,x2,y2,kol;

 x1=Menu->x1; x2=Menu->x2;
 y1=Menu->y1; y2=Menu->y2;
 kol=Menu->kol - 0x01;

 if (Menu->Save) save (x1,y1,x2+1,y2+2);
 ATTR=set_attr (Menu->color1,Menu->bkcolor);
 rev=(15-Menu->bkcolor);

 FILLER=' '; EXPLODE=YES; SHADOW=YES;
 window (x1,y1,x2,y2);
 box (x1,y1,x2,y2,0x01);
 x1++; y1++;
 for (i=0; i<=kol; i++) {
  if ((Item+i)->str[0]) { //Непустая строка
   if ((Item+i)->zam)
    Print (x1+i, y1, Menu->color1, Menu->color2, (Item+i)->str);
   else
    Print (x1+i, y1, rev, rev, (Item+i)->str);
  }
  else HorizLine (x1+i,y1-1,y2,LINE_111); //Пустой пункт
 }
 Fun_Bar (Bar);
 while (1) {
  change_attr (x1+cursor, y1, x1+cursor, y2-1, rev);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (6,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (Mouse_In_Field (x1,y1, 1,y2-y1, kol+1,1, 0, 0, &i, &j) &&
	((Item+i)->str[0])) {
     change_attr (x1+cursor, y1, x1+cursor, y2-1, Menu->bkcolor);
     cursor=i; sim=ENTER;
     change_attr (x1+cursor, y1, x1+cursor, y2-1, rev);
     goto SWITCHER;
    }
    if (MouseX==HorRow) { //Щелчок по горизонтальной строке меню
     for (i=0; i<HorItems; i++) {
      if (MouseY >= Columns[i] && MouseY<= Columns[i+1]-2) {
       buttons=0x00; while (ms_release()<1); // до отпускания кнопки мыши
       Set_Key (2, ESC, HorKeys[i]);
       goto KEYBOARD;
      }
     }
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
SWITCHER:
  change_attr (x1+cursor, y1, x1+cursor, y2-1, Menu->bkcolor);
  switch (sim) {
   case ESC: goto OUT;
   case F1:
    if (Menu->Index==NO_HELP) Help (ABOUT_PULLDOWN);
    else { //Учесть наличие пустых строк
     for (i=0,j=0; i<cursor; i++) if ((Item+i)->str[0]) j++;
     Help (Menu->Index+j);
    }
    Fun_Bar (Bar);
   break;
   case ENTER:
    if ((Item+cursor)->zam) {
     Menu->Setup=cursor;
     (Item+cursor)->sf();
     Fun_Bar (Bar);
    }
   break;
   case UP: case S_TAB:
NEXT_UP:
    if (cursor>0x00) cursor--;
    else cursor=kol;
    if (!((Item+cursor)->str[0])) goto NEXT_UP;
   break;
   case DOWN: case TAB:
NEXT_DOWN:
    if (cursor<kol) cursor++;
    else cursor=0x00;
    if (!((Item+cursor)->str[0])) goto NEXT_DOWN;
   break;
   case PAGE_UP: cursor=0x00;
    while (!((Item+cursor)->str[0])) cursor++; //!!!
   break;
   case PAGE_DO: cursor=kol;
    while (!((Item+cursor)->str[0])) cursor--; //!!!
   break;
   //Переход в соседние меню:
   case LEFT:   Set_Key( 3, ESC, LEFT, ENTER );    break;
   case RIGHT : Set_Key( 3, ESC, RIGHT, ENTER );   break;
   default:
    if (sim!=NO_SIM) {
     i=Find_Key(Menu->keys,(char)sim);
     if (i!=NO_SIM) {
      cursor=i;
      Set_Key (1,ENTER);
     }
    }
   break;
  }
 }
OUT:
 x1--; y1--;
 if (Menu->Save) restore (x1,y1,x2+1,y2+2);
 return (cursor);
}

#define MAX_SWIT_SIZE 23
MODE Saver[MAX_SWIT_SIZE];

MODE Switcher (MENU *Menu, MENUITEM *Item) {
 // Меню-переключатель состояний размерностью до MAX_SWIT_SIZE;
 // возвращает YES по нажатию ENTER, NO_SIM по нажатию ESC
 char *Bar=
  " $ESC$ Отказ  $F1$ Помощь  $DOWN$ Вниз  $UP$ Вверх  $Пробел$ Отметить  $ENTER$ Выбор";
 KEY  Bar_Keys[]={ESC,F1,DOWN,UP,S_BAR,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,33,43,60,72},
      cursor=Menu->Setup,rev,i,j,x1,y1,x2,y2,kol,attr;
 x1=Menu->x1; x2=Menu->x2;
 y1=Menu->y1; y2=Menu->y2;
 kol=Menu->kol - 0x01;
 if (Menu->Save) save (x1,y1,x2,y2);
 ATTR=attr=set_attr (Menu->color1,Menu->bkcolor); // For write_char
 rev=LIGHTGRAY;

 FILLER=' ';
 clear (x1,y1,x2,y2);
 box (x1,y1,x2,y2,0x02);
 x1++; y1++;
 ATTR=attr;
 for (i=0; i<=kol; i++) {
  Saver[i]=(Item+i)->zam;
  Print (x1+i, y1, Menu->color1, Menu->color1, "[ ]");
  if ((Item+i)->zam) {
   CURSOR_OFF();
   write_char (x1+i, y1+1, '■');
   CURSOR_ON();
  }
  Print (x1+i, y1+4, Menu->color1, Menu->color2, (Item+i)->str);
 }
 Fun_Bar (Bar);

 while (1) {
  change_attr (x1+cursor, y1+4, x1+cursor, y2-1, rev);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (6,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (Mouse_In_Field (x1,y1, 1,y2-y1, kol+1,1, 0, 0, &i, &j)) {
     change_attr (x1+cursor, y1+4, x1+cursor, y2-1, Menu->bkcolor);
     cursor=i; sim=S_BAR;
     change_attr (x1+cursor, y1+4, x1+cursor, y2-1, rev);
     goto SWITCHER;
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
SWITCHER:
  change_attr (x1+cursor, y1+4, x1+cursor, y2-1, Menu->bkcolor);
  switch (sim) {
   case ESC:
    for (i=0; i<=kol; i++) (Item+i)->zam=Saver[i];
    cursor=NO_SIM; goto OUT;
   case F1:
    if (Menu->Index==NO_HELP) Help (ABOUT_SWITCHER);
    else Help (Menu->Index+cursor);
    Fun_Bar (Bar);
   break;
   case S_BAR:
    ((Item+cursor)->zam)^=0x01;
    ATTR=attr; CURSOR_OFF();
    if ((Item+cursor)->zam) write_char (x1+cursor, y1+1, '■');
    else write_char (x1+cursor, y1+1, ' ');
    CURSOR_ON();
   break;
   case ENTER:
    cursor=YES; goto OUT;
   case UP: case S_TAB:
    if (cursor>0x00) cursor--;
    else cursor=kol;
   break;
   case DOWN: case TAB:
    if (cursor<kol) cursor++;
    else cursor=0x00;
   break;
   case PAGE_UP: cursor=0x00; break;
   case PAGE_DO: cursor=kol; break;
   default:
    i=Find_Key(Menu->keys,(char)sim);
    if (i!=NO_SIM) {
     cursor=i;
     Set_Key (1,S_BAR);
    }
   break;
  }
 }
OUT:
 x1--; y1--;
 if (Menu->Save) restore (x1,y1,x2,y2);
 return (cursor);
}

MODE ListSwitcher (MENU *Menu, MENUITEM *Item, LIST *List) {
 // Меню выбора из нескольких состояний размерностью до MAX_SWIT_SIZE;
 // возвращает YES по нажатию ENTER, NO_SIM по нажатию ESC
 char *Bar=
  " $ESC$ Отказ  $F1$ Помощь  $DOWN$ Вниз  $UP$ Вверх  $Пробел$ Изменить  $ENTER$ Выбор";
 KEY  Bar_Keys[]={ESC,F1,DOWN,UP,S_BAR,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,33,43,60,72},
      cursor=Menu->Setup,rev,i,j,k,maxj,x1,y1,x2,y2,kol,attr;
 x1=Menu->x1; x2=Menu->x2;
 y1=Menu->y1; y2=Menu->y2;
 kol=Menu->kol - 0x01;
 if (Menu->Save) save (x1,y1,x2,y2);
 ATTR=attr=set_attr (Menu->color1,Menu->bkcolor); // For write_char
 rev=LIGHTGRAY;

 FILLER=' ';
 clear (x1,y1,x2,y2);
 box (x1,y1,x2,y2,0x02);
 x1++; y1++;
 ATTR=attr;
 maxj=0;
 for (i=0; i<=kol; i++) {
  Saver[i]=(List+i)->zam;
  j=strlen__ ((Item+i)->str, &k);
  if (j>maxj) maxj=j;
  Print (x1+i, y1, Menu->color1, Menu->color2, (Item+i)->str);
 }
 maxj++;
 for (i=0; i<=kol; i++) {
  j=(List+i)->zam;
  Print (x1+i, y1+maxj, Menu->color2, Menu->color2, (List+i)->str[j]);
 }
 Fun_Bar (Bar);
 
 while (1) {
  change_attr (x1+cursor, y1, x1+cursor, y2-1, rev);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (6,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (Mouse_In_Field (x1,y1, 1,y2-y1, kol+1,1, 0, 0, &i, &j)) {
     change_attr (x1+cursor, y1, x1+cursor, y2-1, Menu->bkcolor);
     cursor=i; sim=S_BAR;
     change_attr (x1+cursor, y1, x1+cursor, y2-1, rev);
     goto SWITCHER;
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
SWITCHER:
  change_attr (x1+cursor, y1, x1+cursor, y2-1, Menu->bkcolor);
  switch (sim) {
   case ESC:
    for (i=0; i<=kol; i++) (List+i)->zam=Saver[i];
    cursor=NO_SIM; goto OUT;
   case F1:
    if (Menu->Index==NO_HELP) Help (ABOUT_LISTSWITCHER);
    else Help (Menu->Index+cursor);
    Fun_Bar (Bar);
   break;
   case S_BAR:
    if ( (Item+cursor)->zam ) {
     j = (List+cursor)->zam;
     j++;
     if ( j == (List+cursor)->kol  ) j = 0x00;
     ATTR=attr;
     for (i=y1+maxj; i<y2; i++) {
      CURSOR_OFF();
      write_char (x1+cursor, i, ' ');
      CURSOR_ON();
     }
     Print (x1+cursor, y1+maxj, Menu->color2, Menu->color2, (List+cursor)->str[j]);
     (List+cursor)->zam = j;
    }
   break;
   case ENTER:
    cursor=YES; goto OUT;
   case UP: case S_TAB:
    if (cursor>0x00) cursor--;
    else cursor=kol;
   break;
   case DOWN: case TAB:
    if (cursor<kol) cursor++;
    else cursor=0x00;
   break;
   case PAGE_UP: cursor=0x00; break;
   case PAGE_DO: cursor=kol; break;
   default:
    i=Find_Key(Menu->keys,(char)sim);
    if (i!=NO_SIM) {
     cursor=i;
     Set_Key (1,S_BAR);
    }
   break;
  }
 }
OUT:
 x1--; y1--;
 if (Menu->Save) restore (x1,y1,x2,y2);
 return (cursor);
}

void MessageBox(void) {
 WINDOW Win={ 5, 15, 10, 64, " Об окне сообщений ",
 "      Нажмите $ENTER$ или $ESC$, чтобы убрать с эк-\n" //MessageText
 "рана окно сообщений.",
 BLACK, YELLOW, CYAN, NO_HELP, NO, 0 };
 Set_Key (1,ENTER);
 save (5,15,11,66);
 MessageText (&Win);
 Fun_Bar (" $ENTER$ Спасибо");
 AnyClick();
 restore (5,15,11,66);
}

MODE MessageText (WINDOW *Text) {
 // Окно вывода сообщений;
 // возвращает NO_SIM по нажатию ESC, YES по нажатию ENTER
 char *Bar=" $ESC$ Отказ  $F1$ Помощь  $ENTER$ Спасибо";
 KEY  Bar_Keys[]={ESC,F1,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,36};
 MODE ret,x1,x2,y1,y2;
 BUTTON Ok={ 0, 0,0, YES, LIGHTCYAN, YELLOW, GREEN, CYAN, ENTER, " $С$пасибо" };

 x1=Text->x1; x2=Text->x2;
 y1=Text->y1; y2=Text->y2;
 if (Text->Save) save (x1,y1,x2,y2);
 ATTR=set_attr (Text->color1, Text->bkcolor);
 SHADOW=NO; EXPLODE=YES;
 window (x1, y1, x2, y2);
 center_string (x1, y1, y2, Text->hdr);
 Print (x1+1, y1+1, Text->color1, Text->color2, Text->str);
 ret=(y1+y2)>>1;
 Ok.x=x2-2; Ok.y1=ret-4; Ok.y2=ret+4; Ok.bodcolor=Text->bkcolor;
 Show_Button (&Ok);
 Fun_Bar (Bar);
 while (1) {
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (3,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (Push_Button (&Ok)) goto KEYBOARD;
   }
  }
KEYBOARD:
  sim=Get_Key();
  switch (sim) {
   case ESC: ret=NO_SIM; goto OUT;
   case F1:
    if (Text->Index==NO_HELP) MessageBox ();
    else Help (Text->Index);
    Fun_Bar (Bar);
   break;
   case ENTER: ret=YES; goto OUT;
   default: if (Find_Key("cCсС",sim)!=NO_SIM) { ret=YES; goto OUT; }
   break;
  }
 }
OUT: if (Text->Save) restore (x1,y1,x2,y2);
 return ret;
}

MODE AnswMenu (MENU *Text) {
 // Окно вывода запроса "Да/Нет";
 // Возвращает NO по выбору "Нет" или нажатию ESC, YES по выбору "Да"
 char *Bar=" $ESC$ Отказ  $F1$ Помощь  $ENTER$ Подтверждение";
 KEY  Bar_Keys[]={ESC,F1,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,42};
 MODE ret,x1,x2,y1,y2,cursor=Text->Setup,rev,attr;
 BUTTON But[2]={
  { 0, 0,0, YES, LIGHTCYAN, YELLOW, GREEN, CYAN, ENTER, "$Д$а" },
  { 0, 0,0, YES, LIGHTCYAN, YELLOW, GREEN, CYAN, ESC, "$Н$ет" }
 };
 x1=Text->x1; x2=Text->x2;
 y1=Text->y1; y2=Text->y2;
 if (Text->Save) save (x1,y1,x2,y2);
 ATTR=set_attr (Text->color1, Text->bkcolor);
 rev=BLACK; attr=Text->bkcolor;
 SHADOW=NO; EXPLODE=YES;
 window (x1, y1, x2, y2);
 Print (x1+1, y1+1, Text->color1, Text->color2, Text->keys);
 ret=(y2-y1)/3-2;
 But[0].y1=y1+ret;  But[0].y2=But[0].y1+2;
 But[1].y2=y2-ret;  But[1].y1=But[1].y2-3;
 for (ret=0; ret<2; ret++) {
  But[ret].x=x2-2;
  But[ret].bodcolor=Text->bkcolor;
  Show_Button (&But[ret]);
 }
 Fun_Bar (Bar);
 while (1) {
  change_attr (x2-2, But[cursor].y1, x2-2, But[cursor].y2, rev);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (3,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    for (ret=0; ret<2; ret++) if (Push_Button (&But[ret])) goto KEYBOARD;
   }
  }
KEYBOARD:
  sim=Get_Key();
  change_attr (x2-2, But[cursor].y1, x2-2, But[cursor].y2, attr);
  switch (sim) {
   case ESC: ret=NO; goto OUT;
   case F1:
    if (Text->Index==NO_HELP) Help (ABOUT_ANSWMENU);
    else Help (Text->Index);
    Fun_Bar (Bar);
   break;
   case LEFT: case S_TAB: case RIGHT: case TAB:
    cursor^=0x01;
   break;
   case ENTER: ret=cursor^0x01; goto OUT;
   default:
    if (Find_Key("дДlL",sim)!=NO_SIM) { ret=YES; goto OUT; }
    else if (Find_Key("нНyY",sim)!=NO_SIM) { ret=NO; goto OUT; }
   break;
  }
 }
OUT: if (Text->Save) restore (x1,y1,x2,y2);
 return ret;
}


void Close_System (void) {
 set_visual_page (0x00);
 textmode();
 if (Mouse) {
  asm mov ah,0;
  asm int 33h;
  CURSOR_OFF();
 }
 asm mov ah,0x4c;
 asm mov al,0; // Код возврата=0
 asm int 21h;  //exit (0);
}

void GetDateString (MODE *buffer, MODE Day, MODE Mon, int Year) {
 //Получает в buffer строку даты в виде "NN месяца NNNN"
 MODE *Month[12]={
  "января","февраля","марта","апреля","мая","июня",
  "июля","августа","сентября","октября","ноября","декабря"
 };
 MODE i,j,k;
 itoa_ (Day,buffer);
 i=strlen_(buffer);
 j=strlen_(Month[Mon-1]);
 buffer[i]=' ';
 for (k=0; k<j; k++) buffer[i+1+k]=Month[Mon-1][k];
 buffer[i+j+1]=' ';
 itoa_(Year,&buffer[i+j+2]);
 return;
}

#define DATE_STR 0
void Init_Date (void) {
 // Выводит строку даты по координатам (DATE_STR,DATE_COL)
 MODE l;
 asm { mov ah,2ah; int 21h ; mov DAY,dl; mov MON,dh; mov YEAR,cx; }
 GetDateString (&buffer[bufpos],DAY,MON,YEAR);
 l=strlen_(&buffer[bufpos]);
 write_string (DATE_STR, 79-l, &buffer[bufpos], 0x70);
 return;
}
