#include <textmenu.h>
#include <function.h>
//#include <stdlib.h>
#include <dos.h>
#include <stdio.h>
//#include <string.h>
#include <dir.h>

#define MAX_LIST 6  /* Макс. поле прокрутки списка для ShowList, строк */
#define MAX_LIST_SAVE 25  /* Макс. размер сохранения списка в ShowList */
// #define MAX_PATH 93  /* Макс. длина пути DOS */
MODE Path[MAX_PATH]; /* Рабочий каталог программы */
MODE PathLen;        /* Длина строки Path */
FILE *HelpFile; /* Дескриптор файла помощи */
MODE HelpFlag=NO; /* Флаг наличия файла помощи */
unsigned HelpStartOffset=0; /* Смещение в файле помощи до начала помощи */

void MoveTopList (MODE **str, MODE newtop) {
 MODE *ptr,i;
 ptr=str[newtop];
 for (i=newtop; i>0; i--) str[i]=str[i-1];
 str[0]=ptr;
}

void MoveAllList (MODE **str, MODE kol, MODE dir) {
 MODE *ptr,i;
 if (dir) { // Сдвинуть вниз
  ptr=str[kol-1];
  for (i=kol-1; i>0; i--) str[i]=str[i-1];
  *ptr='\0'; str[0]=ptr;
 }
 else { // Сдвинуть вверх
  ptr=str[0];
  for (i=0; i<kol-1; i++) str[i]=str[i+1];
  *ptr='\0'; str[kol-1]=ptr;
 } return;
}

MODE DrawHorBar (MODE x, MODE y1, MODE y2) {
 //Вернет начальную позицию горизонтального бегунка окна
 MODE i;
 CURSOR_OFF();
 write_char (x,y1+1,'\021');
 write_char (x,y1+2,'\n');
 write_char (x,y2-1,'\020');
 for (i=y1+3; i<y2-1; i++) write_char (x,i,'▒');
 CURSOR_ON();
 return (y1+2);
}

MODE DrawVertBar (MODE x1, MODE x2, MODE y) {
 //Вернет начальную позицию вертикального бегунка окна
 MODE i;
 CURSOR_OFF();
 write_char (x1+1,y,'\036');
 write_char (x1+2,y,'\n');
 write_char (x2-1,y,'\037');
 for (i=x1+3; i<x2-1; i++) write_char (i,y,'▒');
 CURSOR_ON();
 return (x1+2);
}

MODE ChangeHorBar (int cursor, int kol, MODE beg, MODE x, MODE y1, MODE y2) {
 //Переводит cursor из интервала [0,kol] в величину y из интервала [y1,y2],
 //обновляет на экране позицию бегунка beg, возвращает новую позицию beg
 CURSOR_OFF();
 write_char (x,beg,'▒');
 beg=y1+2+cursor*(y2-y1-3)/kol;
 write_char (x,beg,'\n');
 CURSOR_ON();
 return beg;
}

MODE ChangeVertBar (int cursor, int kol, MODE beg, MODE x1, MODE x2, MODE y) {
 //Переводит cursor из интервала [0,kol] в величину x из интервала [x1,x2],
 //обновляет на экране позицию бегунка beg, возвращает новую позицию beg
 CURSOR_OFF();
 write_char (beg,y,'▒');
 beg=x1+2+cursor*(x2-x1-3)/kol;
 write_char (beg,y,'\n');
 CURSOR_ON();
 return beg;
}

MODE MouseHorBar (MODE x,MODE y1,MODE y2) {
 //Обрабатывает щелчок мышью по вертикальной полосе прокрутки
 if (MouseX==x) {
  if (MouseY==y1+1) {
   Set_Key (1,LEFT); buttons=0; while (!ms_release());
   return YES;
  }
  else if (MouseY==y2-1) {
   Set_Key (1,RIGHT); buttons=0; while (!ms_release());
   return YES;
  }
 }
 return NO;
}


MODE MouseVertBar (MODE x1,MODE x2,MODE y) {
 //Обрабатывает щелчок мышью по вертикальной полосе прокрутки
 if (MouseY==y) {
  if (MouseX==x1+1) {
   Set_Key (1,UP); buttons=0; while (!ms_release());
   return YES;
  }
  else if (MouseX==x2-1) {
   Set_Key (1,DOWN); buttons=0; while (!ms_release());
   return YES;
  }
 }
 return NO;
}

MODE ShowList (MODE x1,MODE y1,MODE kol,MODE maxlen,MODE **str,
 MODE color, MODE bkcolor, MODE PushOnTop) {
 //Возвращает NO_SIM - ошибка или нажатие ESC,
 // иначе - номер выбранного элемента в списке
 //Если PushOnTop равен 1, помещает выбранный элемент на вершину списка
 MODE *Bar=" $ESC$ Отказ  $DOWN$ Вниз  $UP$ Вверх  $ENTER$ Выбрать";
 KEY Bar_Keys[]={ESC,DOWN,UP,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,32,46},*Buf[MAX_LIST_SAVE];
 MODE x2,y2,attr1,cursor=0,i,j,start,end,rev,beg,ret=NO_SIM;

 if (kol<2) return ret;
// show_cursor(NO);
 x2=x1+1+(kol>MAX_LIST?MAX_LIST:kol);
 y2=y1+maxlen+1;
 start=0;
 if (PushOnTop) for (i=0; i<kol; i++) Buf[i]=str[i];
 end=MAX_LIST;
 if (end>kol) end=kol;

 save (x1,y1,x2,y2);
 ATTR=attr1=set_attr (color, bkcolor);
 rev=(~bkcolor)&0x07;
 set_attr (bkcolor|0x08, color&0x7f);
 FILLER=' ';
 clear (x1,y1,x2,y2);
 box (x1,y1,x2,y2,0x02);
 beg=DrawVertBar (x1,x2,y2);
 x1++; y1++;
 Fun_Bar (Bar);
AGAIN:
 ATTR=attr1;
 for (i=start; i<end; i++) {

  clear (x1+i-start,y1,x1+i-start,y2-1);
//!!! CURSOR_OFF();
//  for (j=y1; j<y2; j++) write_char (x1+i-start,j,' ');
//  CURSOR_ON();
  write_string (x1+i-start,y1,str[i],attr1);
 }
 while (1) {
  change_attr (x1+cursor-start, y1, x1+cursor-start, y2-1, rev);
  if (kol>3) beg=ChangeVertBar (cursor,kol,beg,x1-1,x2,y2);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (4,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (MouseVertBar (x1-1,x2,y2)) goto KEYBOARD;
                                      //!!!kol
    if (Mouse_In_Field (x1,y1, 1,y2-y1, end-start+1,1, 0, 0, &i, &j)) {
     change_attr (x1+cursor-start, y1, x1+cursor-start, y2-1, bkcolor);
     cursor=start+i; sim=ENTER;
     change_attr (x1+cursor-start, y1, x1+cursor-start, y2-1, rev);
     goto SWITCHER;
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
SWITCHER:
  change_attr (x1+cursor-start, y1, x1+cursor-start, y2-1, bkcolor);
  switch (sim) {
   case ESC:
    if (PushOnTop) for (i=0; i<kol; i++) str[i]=Buf[i];
   goto OUT;
   case F1:
//    if (PushOnTop) { //!!!
     Help (ABOUT_SHOW_LIST);  // Вызов помощи связываем со свойством
     Fun_Bar (Bar);           // перемещения выбранной записи на вершину
//    } // списка. Это не нужно только в самой помощи (экономим параметр)
   break;
   case ENTER:
    if (PushOnTop && (start>0 || cursor>0)) {
     MoveTopList (str,cursor);
    }
    ret=cursor;
   goto OUT;
   case UP:
    if (cursor>start) cursor--;
    else if (start>0) { cursor--; start--; end--; goto AGAIN; }
   break;
   case DOWN:
    if (cursor<end-1) cursor++;
    else if (end<kol) { start++; end++; cursor++; goto AGAIN; }
   break;
   case PAGE_UP: cursor=start; break;
   case PAGE_DO: cursor=end-1; break;
  }
 }
OUT:
 x1--; y1--;
 restore (x1,y1,x2,y2);
// show_cursor(YES);
 return (ret);
}


void InitPath (MODE *StartName) { // Параметр - строка запуска
 MODE *Ptr; int j;
 if (*StartName) { // Строка не пуста
  Ptr=strrchr_ (StartName,'\\'); //Есть путь
  if (Ptr==NULL) {
   Ptr=strrchr_ (StartName,'/'); //Другая возможность записать путь
   if (Ptr==NULL) goto GET_WAY;
   else goto GET_NUL;
  }
  else {
GET_NUL: *(++Ptr)='\0';
  }
  strcpy_ (Path,StartName);
 }
 else {
GET_WAY: getcwd (Path,MAX_PATH); // Set current directory
 }
 j=strlen_ (Path);
 if (Path[j-1]!='\\') { Path[j]='\\'; j++; Path[j]='\0'; }
 PathLen=j; return;
}

MODE Go_Work (void) {
 // Пытается перейти в рабочий каталог программы.
 // Возвращает ноль - ошибка.
 int Res;
 if (PathLen>3) Path[PathLen-1]='\0';
 Res=chdir (Path); // Path[PathLen] - рабочая директория
 if (PathLen>3) Path[PathLen-1]='\\';
 if (Res) {  return NO; } // Не найден рабочий каталог
 else return YES;
}

MODE OpenHelp (MODE *HelpName) {
 if (!Go_Work()) return NO;
 HelpFile= fopen (HelpName,"r+b");
 if (HelpFile==NULL) return NO;
 return YES;
}

struct HELP {
 MODE active;    // Наличие пункта
 MODE hdr[49];   // Заголовок темы + '\0'
 int  kol,       // Количество строк текста
      len;       // Длина строки
 long offset;    // Смещение до строки
}; // 58 байт

void GetHelp (unsigned Num, HELP *Now, MODE *Mess) {
 // Получить предопределенную статью помощи
 MODE *Messages[]={ //Ширина 48, до 25 строк
"     Вы можете вводить строку текста  допустимой\n"    //GetString
"длины. При этом Вам  доступны  следующие возмож-\n"
"ности:\n"
" Завершить ввод строки - клавиша $ENTER$;\n"
" Отказаться от ввода   -         $ESC$;\n"
" Режим Вставка/Замена  -         $INS$;\n"
" Удалить символ под курсором   - $DEL$;\n"
" Удалить символ перед курсором - $BACKSPACE$;\n"
" При достижении максимальной длины строки справа\n"
"от поля ввода появляется значок ''.",

"      Если полей ввода  несколько,  перемещаться\n"    //GetText
"между ними можно нажатием $TAB$ (вперед) или  ком-\n"
"бинации клавиш $Shift-TAB$ (назад).\n"
" Нажав стрелку $вниз$, можно раскрыть список  \"ис-\n"
"тории\" данного поля ввода. Нажав в поле  стрелку\n"
"влево или вправо или $пробел$, Вы перейдете к  ре-\n"
"дактированию данного поля, а нажатие любой  дру-\n"
"гой клавиши $очистит$ поле и, если нажата алфавит-\n"
"но-цифровая клавиша, введет соответствующий сим-\n"
"вол в строку.",

"       Листать список можно клавишами со  стрел-\n"    //ShowList
"ками $вверх$/$вниз$, выбрать из списка нажатием кла-\n"
"виши $ENTER$, а отказаться от выбора нажатием $ESC$.\n"
"Выбранный элемент после  этого  перемещается  на\n"
"вершину списка.",

"      Вы можете перемещаться по дереву каталогов\n"    //Filer
"и просматривать список файлов  по выбранной Вами\n"
"маске.\n"
" Нажатие $ENTER$ подтверждает выбор;\n"
" $ESC$ - отмена выбора файла;\n"
" $F2$  - просмотреть оглавление другого диска.",

"      Для выбора пункта меню используйте клавиши\n" //Popup
"$\030$,$\031$. Для подтверждения нажмите $ENTER$. $ESC$ убира-\n"
"ет меню с экрана. Перейти в соседние меню  можно\n"
"клавишами $\033$,$\032$.",

"      Для выбора пункта меню используйте клавиши\n" //HorMenu
"$\033$,$\032$. После нажатия $ENTER$ или $2$  раскрывается па-\n"
"дающее меню, если оно есть.\n"
"      Для выхода из меню используйте $ESC$.",

"      Для выбора пункта меню используйте клавиши\n" //PullDown
"$\030$,$\031$. Для подтверждения нажмите $ENTER$. $ESC$ убира-\n"
"ет меню с экрана.",

"      Для выбора переключателя используйте  кла-\n" //Switcher,
"виши $\030$,$\031$. Для  изменения состояния переключателя\n" //ListSwitcher
"используйте клавишу пробела.  Подтвердить  уста-\n"
"новки можно нажатием $ENTER$, отменить все измене-\n"
"ния можно нажатием $ESC$.",

"      Выберите вариант ответа  клавишами  $\033$, $\032$ и\n" //AnswMenu
"нажмите $ENTER$. Нажатие $ESC$ соответствует  ответу\n"
"\"Нет\".",

"      Выберите требуемый диск клавишами $\033$, $\032$ и на-\n"   //SelectDrive
"жмите $ENTER$ или нажмите клавишу с буквой, соответ-\n"
"ствующей имени диска."
};
 MODE *Headers[]={
 "Ввод строки",
 "Окно ввода",
 "Список",
 "Выбор файла",
 "Меню выбора",
 "Строка меню",
 "Падающее меню",
 "Переключатель",
 "Выбор ответа",
 "Выбор диска"
};
 MODE i;
 strcpy_ (Mess, Messages[Num]);
 strcpy_ (&Now->hdr[0], Headers[Num]);
 Now->len= strlen__ ( Mess,&i);
 Now->kol=i;
 return;
}

MODE ReadHelp (unsigned Index, HELP *Now, MODE *Mess) {
 int i;
 if (!HelpFlag) return NO;
 fseek (HelpFile, HelpStartOffset+Index*58L, SEEK_SET);
 i=fread (Now,58,1,HelpFile);
 if (i==0) return NO;
 fseek (HelpFile, Now->offset, SEEK_SET);
 if (Now->len > MAX_HELP)
  Now->len = MAX_HELP;
 i=fread (&Mess[0],Now->len,1,HelpFile);
 Mess[Now->len]='\0';
 if (i==0) return NO;
 return YES;
}

MODE ReadHeaders (void) {
 static MODE IsStat[KOL_HELP]; // Информация о наличии статей помощи
 static MODE IsHdr; // Было ли считывание информации из файла
 static MODE Count; // Длина списка статей
 static MODE *Str[KOL_HELP];    // указатели на строки заголовков
 static MODE Buf[KOL_HELP*49]; // размер буфера для заголовков помощи
 static MODE **Ptr=&Str[0];
 HELP Now;
 MODE i,c,k;

 fseek (HelpFile, HelpStartOffset, SEEK_SET);
 if (!IsHdr) {
  for (i=0,Count=0; i<KOL_HELP; i++) {
   c=fread (&Now,58,1,HelpFile);
   if (c==0) return NO_SIM;
   if (Now.active) {
    Str[Count]=&Buf[49*Count];
    strcpy_ (Str[Count],Now.hdr);
    //LeftSide (Str[k],48);
    IsStat[i]=YES;
    Count++;
   }
  }
  IsHdr=YES;
 }
 c=ShowList (6, 15, Count, 48, Ptr, YELLOW, CYAN, NO); // Вернули номер в списке
 for (i=0,k=0; i<KOL_HELP; i++) {
  if (IsStat[i]) {
   if (k==c) return i; // Возвращаем реальный индекс помощи
   k++;
  }
 }
 return NO_SIM;
}

//Функции для обслуживания файла помощи (чтение и запись данных)
void InitFileData (void) {
 fseek (HelpFile, 0L, SEEK_SET);
}
void ShutFileData (void) {
 fseek (HelpFile, 0L, SEEK_END);
 fclose (HelpFile);
}
MODE ReadByte (void) {
 MODE Byte;
 fread (&Byte,1,1,HelpFile);
 return Byte;
}
int ReadInt (void) {
 int Int;
 fread (&Int,2,1,HelpFile);
 return Int;
}
unsigned ReadWord (void) {
 unsigned Word;
 fread (&Word,2,1,HelpFile);
 return Word;
}
MODE *ReadString (unsigned Len, MODE *Str) {
 unsigned Res;
 Res=fread (Str,Len,1,HelpFile);
 if (Res) return Str;
 return 0;
}

void WriteByte (MODE Byte) { fwrite (&Byte,1,1,HelpFile); return; }

void WriteInt (int Int) { fwrite (&Int,2,1,HelpFile); return; }

void WriteWord (unsigned Word) {  fwrite (&Word,2,1,HelpFile); return; }

void WriteString (unsigned Len, MODE *Str) { fwrite (Str,Len,1,HelpFile); return; }
////////////////////////////////////////////////////////////////

void About_Help_System (void) {
 WINDOW Win={ 6, 15, 18, 64, " О системе помощи ",
"     Система  помощи,  используемая  программой,\n"
"состоит из отдельных статей, вызываемых  в  кон-\n"
"текстном режиме. Пролистать статью можно исполь-\n"
"зуя клавиши со стрелками вверх и вниз, а  стрел-\n"
"ки влево и вправо позволяют выбрать одну из кно-\n"
"пок \"Спасибо\" и \"Указатель\". Кнопка  \"Указатель\"\n"
"выводит на экран оглавление помощи.\n"
"Клавиши $TAB$ и $Shift-TAB$ позволяют перейти к сле-\n"
"дующей и предыдущей подходящей статье.",
  BLACK, YELLOW, CYAN, NO_HELP, YES, 0 };
 MessageText (&Win);
}

void Help (MODE Index) {
 MODE Buffer[MAX_HELP]; //!!! Буфер для текста помощи
 MODE x1,y1,x2,y2,beg,rev,color1,color2,bkcolor,cursor=0x00;
 BUTTON But[2]={
  { 0, 0,0, YES, LIGHTCYAN, YELLOW, GREEN, CYAN, ENTER, " $С$пасибо" },
  { 0, 0,0, YES, LIGHTCYAN, YELLOW, GREEN, CYAN, ESC,   " $У$казатель" }
 };
 MODE *Bar=" $ESC$ Отказ  $TAB$ Следующий  $Shift-TAB$ Предыдущий  $ENTER$ Выбор";
 KEY  Bar_Keys[]={ESC,TAB,S_TAB,ENTER},sim;
 MODE Pos_Keys[]={0,11,26,48,60},scrx,Dir,Pred=0x02;
 HELP Now;
 MODE Byte,Saver;
 WINDOW NoHelp={ 11, 15, 15, 64, " Помощь ",
  "Файл помощи отсутствует или не найдено сообщение\n" ,
  BLACK, YELLOW, CYAN, NO_HELP, YES, 0 };
 int Pos,start,i;

 y1=15; y2=64; Saver=ATTR;
 x1=4;  x2=20; scrx=x2-x1-4;
 color1=BLACK; color2=YELLOW; bkcolor=CYAN;
 EXPLODE=YES; SHADOW=YES;
 save (x1,y1,x2+1,y2+2);
 ATTR=set_attr (WHITE,bkcolor);
 rev=BLACK;
 window (x1,y1,x2,y2);
 center_string (x1, y1, y2, " Помощь ");

 if (Index==GOTO_INDEX) goto PRED2;
 if (Index>KOL_HELP) {
  Pred=0x01; //Предопределенная статья
  i=((y2-y1)>>1)-4;
 }
 else { //Статья Указателя
PRED2:
  i=(y2-y1)>>3;
 }
 But[0].y1=y1+i;  But[0].y2=But[0].y1+8;
 But[1].y2=y2-i;  But[1].y1=But[1].y2-10;
 for (i=0; i<Pred; i++) {
  But[i].x=x2-2;
  But[i].bodcolor=bkcolor;
  Show_Button (&But[i]);
 }
 Fun_Bar (Bar);
 x1++; y1++;
 if (Index==GOTO_INDEX) goto INDEX; //Сразу показать индекс

READ_STAT:
 Set_Window (x1,y1,x2-3,y2-1);
 if (Pred==0x01) // Предопределенная статья
  GetHelp (Index-KOL_HELP-1, &Now,&Buffer[0]);
 else if (Index==NO_HELP || (!ReadHelp (Index,&Now,&Buffer[0]))) {
  Set_Window (0,0,24,79);
  restore (x1-1,y1-1,x2+1,y2+2);
  MessageText (&NoHelp); //Сообщение "Нет помощи"
  goto OUT_2;
 }
 ATTR=set_attr (WHITE,bkcolor);
 FILLER=' '; clear (x1,y1,x2-3,y2-1);
 Print (0, 0, WHITE,WHITE, Now.hdr);
 Pos=0;
 Dir=0x02; start=0;
 Print (1,0,color1,color2,&Buffer[0]);
 beg=DrawVertBar (x1-1,x2,y2);
READ_TEXT:
 if (Dir==UP_DIR) {
  for (i=Pos-2; i>=0; i--) {
   if (i==0) goto SET_POS; //Для вывода 1 строки текста
   if (Buffer[i]=='\n') {
    i++;
SET_POS:
    ATTR=set_attr (WHITE,bkcolor); FILLER=' '; clear (x1+1,y1,x2-3,y2-1);
    Print (1,0,color1,color2,&Buffer[i]);
    break;
   }
  }
  Pos=i;
 }
 else if (Dir==DOWN_DIR) {
  for (i=Pos; i<Now.len; i++) {
   if (Buffer[i]=='\n') {
    i++;
    ATTR=set_attr (WHITE,bkcolor); FILLER=' '; clear (x1+1,y1,x2-3,y2-1);
    Print (1,0,color1,color2,&Buffer[i]);
    break;
   }
  }
  Pos=i;
 }
 while (1) {
  change_attr (x2-2, But[cursor].y1, x2-2, But[cursor].y2, rev);
  if (Now.kol>scrx) {
   Byte=ATTR; //!!!
   ATTR=set_attr (WHITE,bkcolor);
//   CURSOR_OFF();   write_char (beg,y2,'▒');
//   beg=x1+1+(start)*(x2-x1-3)/(Now.kol-scrx); if (beg>x2-2) beg=x2-2; //!!!
//   write_char (beg,y2,'\n'); CURSOR_ON();
   beg=ChangeVertBar (start,Now.kol,beg,x1,x2,y2);
   ATTR=Byte;
  }
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (4,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    for (i=0; i<Pred; i++) if (Push_Button (&But[i])) goto KEYBOARD;
    if (MouseVertBar (x1-1,x2,y2)) goto KEYBOARD;
   }
  }
KEYBOARD:
  sim=Get_Key();
  change_attr (x2-2, But[cursor].y1, x2-2, But[cursor].y2, But[cursor].bkcolor);
  switch (sim) {
   case ESC: goto OUT;
   case F1:
    Set_Window (0,0,24,79);
    if (Pred==0x02) About_Help_System();
    else MessageBox(); //Для предопределенной статьи не выводим окно о помощи
    Fun_Bar(Bar);
    Set_Window (x1,y1,x2-3,y2-1);
   break;
   case UP:
    if (start>0) { start--; Dir=UP_DIR; goto READ_TEXT; }
   break;
   case DOWN:
    if (start<=Now.kol-scrx) { start++; Dir=DOWN_DIR; goto READ_TEXT; }
   break;
   case LEFT: case RIGHT:
    if (Pred==0x02) cursor^=0x01;
   break;
   case S_TAB: Set_Key (3,ESC,S_TAB,F1); break;
   case TAB: Set_Key (3,ESC,TAB,F1); break;
   case ENTER:
    if (cursor) { //Указатель
     ATTR=set_attr (WHITE,bkcolor);
//     CURSOR_OFF();     write_char (beg,y2,'▒');       CURSOR_ON();
     Set_Window (0,0,24,79);
INDEX:
     Byte=ReadHeaders (); //Прочитать указатель
     Fun_Bar (Bar);
//     Set_Window (x1,y1,x2-3,y2-1);
     if (Byte!=NO_SIM) Index=Byte; //!!!
//     else MessageText (&NoHelp);
     goto READ_STAT;
    }
    else goto OUT;
   //break;
   case HOME: if (Pred==0x02) cursor=0x00; break;
   case END:  if (Pred==0x02) cursor=0x01; break;
   default:
    if (sim==ALT_C || Find_Key("cCсС",sim)!=NO_SIM) goto OUT;
    else if (Pred==0x02 && (sim==ALT_E || Find_Key("eEуУ",sim)!=NO_SIM)) Set_Key (2,END,ENTER);
   break;
  }
 }
OUT:
 Set_Window (0,0,24,79);
 x1--; y1--;
 restore (x1,y1,x2+1,y2+2);
OUT_2:
 ATTR=Saver;
}

MODE GetString (MODE x1, MODE y1, MODE *Strin, MODE MaxLen,
 MODE LineColor, MODE BkColor) {
 // Вернет 0x00 - ESC, 0x01 -ENTER
 MODE Curs_Pos=0,Len=0,Cursor=0x00,First=YES,i,attr1;
 MODE *Bar=" $ESC$ Отказ  $F1$ Помощь  $INS$ Режим  $ENTER$ Ввести";
 KEY Bar_Keys[]={ESC,F1,INS,ENTER},c;
 MODE Pos_Keys[]={0,11,22,33,46},Ret;

 Fun_Bar (Bar);
 Len= strlen_ (Strin);
 if (Len>MaxLen) {
  Len=MaxLen;
  Strin[MaxLen]='\0';
 }
 Curs_Pos=0;
 FILLER=' ';
 ATTR=attr1=set_attr (LineColor, BkColor);
 //attr2=set_attr (BkColor, BkColor);
 clear (x1,y1,x1,y1+MaxLen);
 do {
  show_cursor (YES);
AGAIN:
  Strin[Len]='\0';
  write_string (x1,y1,Strin,attr1); CURSOR_OFF();
  for (i=y1+Len; i<y1+MaxLen; i++) write_char (x1,i,' ');
  goto_xy (x1,y1+Curs_Pos);
  if (Len==MaxLen)  write_char (x1,y1+MaxLen+1,''); // Предупреждение
  else write_char (x1,y1+MaxLen+1,' ');              // о конце строки
  CURSOR_ON();
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (4,Bar_Keys,Pos_Keys)) goto KEYBOARD;
   }
  }
KEYBOARD:
  c=Get_Key ();
  //write_string (x1,y1,Strin,attr2);
  //goto_xy (x1,y1+Curs_Pos);
  if (First) {
   First=NO;
   if (Correct_String (Strin,c,0)) {
    Strin[0]=c; Len=1; Curs_Pos=1;
   }
   else Set_Key (1,c);
   goto AGAIN;
  }
  switch (c) {
   case ESC:   Ret= 0; goto OUT;
   case F1:
    show_cursor(NO);
    Help (ABOUT_INPUT_STRING);
    show_cursor(YES);
    Fun_Bar (Bar);
   break;
   case ENTER: Ret= 1; goto OUT;
   case LEFT: if (Curs_Pos>0) Curs_Pos--; break;
   case RIGHT: if (Curs_Pos<Len) Curs_Pos++; break;
   case HOME: Curs_Pos=0; break;
   case END: Curs_Pos=Len; break;
   case INS:
    Cursor^=0x01;
    size_cursor (Cursor);
   break;
   case DEL: if (Len > 0) {
    if (Curs_Pos>=0 && Curs_Pos<Len) {
     for( i = Curs_Pos; i < Len-1; i++ ) Strin[i] = Strin[i+1];
     Len--;
    }
   } break;
   case BS: if (Len > 0) {
    if (Curs_Pos>0 && Curs_Pos<=Len) {
     for( i = Curs_Pos-1; i < Len-1; i++ ) Strin[i] = Strin[i+1];
      Len--; Curs_Pos--;
    }
   } break;
   default:
   if (isascii(c) && Correct_String (Strin,c,Curs_Pos)) {
    if (Cursor) { //Замена
     if (Curs_Pos<=Len) {
      Strin[Curs_Pos]=c;
      if (Curs_Pos<MaxLen) Curs_Pos++;
      if (Curs_Pos>Len) if (Len<MaxLen) Len++;
     }
    }
    else if (Len<MaxLen) { // Вставка
     for (i=Len; i>Curs_Pos; i--) Strin[i]=Strin[i-1];
     Strin[Curs_Pos]=c;
     Len++;
     if (Curs_Pos<Len)Curs_Pos++;
    }
   }
   //else { Ret=c; goto OUT; }
   break;
  }
 //!!! Strin[Len]='\0';
 } while (1);
OUT:
 write_string (x1,y1,Strin,attr1);
 size_cursor (NO); show_cursor (NO);
 return (Ret);
}


MODE GetText (MENU *Menu, STRLIST *Items) {
 char *Bar=" $ESC$ Отказ  $F1$ Помощь  $TAB$ Следующее  $DOWN$ Раскрыть  $ENTER$ Подтверждение";
 KEY  Bar_Keys[]={ESC,F1,TAB,DOWN,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,37,52,72};
 MODE ret,x1,x2,y1,y2,kol,cursor=0x00,rev,attr,i;
 BUTTON But[2]={
  { 0, 0,0, YES, LIGHTCYAN, YELLOW, GREEN, CYAN, ENTER, "$С$пасибо" },
  { 0, 0,0, YES, LIGHTCYAN, YELLOW, GREEN, CYAN, ESC, " $О$тказ" }
 };
 x1=Menu->x1; x2=Menu->x2;
 y1=Menu->y1; y2=Menu->y2;
 kol=Menu->kol;
 if (Menu->Save) save (x1,y1,x2,y2);
 ATTR=set_attr (Menu->color1, Menu->bkcolor);
 rev=BLACK;
 SHADOW=NO; EXPLODE=YES;
 window (x1, y1, x2, y2);
 Print (x1+1, y1+1, Menu->color1, Menu->color2, Menu->keys);
 Correct_String=Correct_Ascii;
 for (i=0; i<kol; i++) {
  ATTR=set_attr (Menu->color1, Menu->bkcolor);
  Print ((Items+i)->x,(Items+i)->y, (Items+i)->color,(Items+i)->bkcolor,
   (Items+i)->hdr);
  Set_Key (1,ENTER); //!!! Set_Insert_Key (1,ENTER);
  GetString ((Items+i)->x1, (Items+i)->y1, (Items+i)->str[0],
             (Items+i)->maxlen, (Items+i)->color, (Items+i)->bkcolor);
  ATTR=set_attr ((Items+i)->color,Menu->bkcolor);
  CURSOR_OFF();
  if ((Items+i)->kol >1)
  write_char ((Items+i)->x1, (Items+i)->y1+(Items+i)->maxlen+2,'\037');
  CURSOR_ON();
 }
 ret=(y2-y1)/3-4;
 But[0].y1=y1+ret;  But[0].y2=But[0].y1+7;
 But[1].y2=y2-ret;  But[1].y1=But[1].y2-6;   
 for (ret=0; ret<2; ret++) {
  But[ret].x=x2-2;
  But[ret].bodcolor=Menu->bkcolor;
  Show_Button (&But[ret]);
 }
 Fun_Bar (Bar);
 if (Menu->Setup) cursor=0x00; // Разрешен ввод
 else cursor=kol;              // Запрещен ввод
 while (1) {
  if (cursor>=kol) {
   show_cursor(NO);
   change_attr (x2-2, But[cursor-kol].y1, x2-2, But[cursor-kol].y2, rev);
  }
  else {
   show_cursor(YES);
CURS_POS:
   goto_xy ((Items+cursor)->x1,(Items+cursor)->y1);
  }
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (5,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    for (ret=0; ret<2; ret++) if (Push_Button (&But[ret])) goto KEYBOARD;
    for (i=0; i<kol; i++) if (MouseX==(Items+i)->x1 &&
     MouseY>=(Items+i)->y1 && MouseY<=(Items+i)->y1+(Items+i)->maxlen+2) {
     cursor=i;
     buttons=0x00; while (!ms_release());
     Set_Key (1,DOWN); goto CURS_POS;
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
  if (cursor>=kol) {
   attr=But[cursor-kol].bkcolor;
   change_attr (x2-2, But[cursor-kol].y1, x2-2, But[cursor-kol].y2, attr);
  }
  switch (sim) {
   case ESC: ret=NO; goto OUT;
   case F1:
    show_cursor(NO);
    if (cursor>=kol || Menu->Index==NO_HELP)  Help (ABOUT_INPUT_WINDOW);
    else Help (Menu->Index + cursor);
    show_cursor(YES);
    Fun_Bar (Bar);
   break;
   case S_TAB:
    if (cursor>0) cursor--;
    else cursor=kol+1;
   break;
   case TAB:
    cursor++;
    if (cursor==kol+2) cursor=0;
   break;
   case ENTER:
    if (cursor==kol+1) ret=NO;
    else ret=YES;
   goto OUT;
   case LEFT:
    if (cursor>=kol) { cursor--; break; }
    else goto DEFAULT;
   case RIGHT:
    if (cursor<kol) goto DEFAULT;
    else if (cursor==kol) cursor++;
    else cursor=0;
   break;
   case DOWN:
    if (Menu->Setup && cursor<kol && (Items+cursor)->kol >1) {
     ShowList ((Items+cursor)->x,(Items+cursor)->y-1, (Items+cursor)->kol,
      (Items+cursor)->maxlen+1,(Items+cursor)->str,
      (Items+cursor)->color, (Items+cursor)->bkcolor,YES);
RENEW:
     Set_Key (1,ENTER); //!!! Set_Insert_Key (1,ENTER);
     GetString ((Items+cursor)->x1, (Items+cursor)->y1, (Items+cursor)->str[0],
                (Items+cursor)->maxlen, (Items+cursor)->color, (Items+cursor)->bkcolor);
     Fun_Bar (Bar);
    }
   break;
   default:
DEFAULT:
    if (sim==ALT_C) { ret=YES; goto OUT; }
    else if (sim==ALT_J) { ret=NO; Set_Key (1,ESC); }
    else for (i=0; i<kol; i++) if (sim==(Items+i)->key) { cursor=i; break; }
    else if (Menu->Setup && cursor<kol &&
     (isascii(sim) || sim==LEFT || sim==RIGHT)) { // Допустимый символ
     if (sim==S_BAR) Set_Key (1,HOME); // обрабатываем существующую строку
     else if (sim==LEFT || sim==RIGHT) Set_Key (1,sim);
     else {
      Set_Key (1,sim);
      if ((Items+cursor)->kol>1) {
       MoveAllList ((Items+cursor)->str,(Items+cursor)->kol,DOWN_DIR);
      }
     }
     sim=GetString ((Items+cursor)->x1,(Items+cursor)->y1,(Items+cursor)->str[0],
      (Items+cursor)->maxlen,(Items+cursor)->color,(Items+cursor)->bkcolor);
     if (sim==0 && (Items+cursor)->kol>1) // вернули ESC
      MoveAllList ((Items+cursor)->str,(Items+cursor)->kol,UP_DIR);
//     else //вернули ENTER
//      Set_Key (1,TAB); //!!!
     goto RENEW;
    }
   break;
  }
 }
OUT: if (Menu->Save) restore (x1,y1,x2,y2);
 show_cursor (NO);
 return ret;
}

///////////////////////////////////////////////
/*
void Test_Text (void) {
 MENU Menu={4,20,15,65, 3, " Окно ввода текстов:", WHITE,LIGHTRED,BLUE, 0, YES,YES};
 MODE *TigersStr[12]={
  "1\0\0\0\0\0\0\0",
  "2\0\0\0\0\0\0\0",
  "3\0\0\0\0\0\0\0",
  "4\0\0\0\0\0\0\0",
  "5\0\0\0\0\0\0\0",
  "6\0\0\0\0\0\0\0",
  "7\0\0\0\0\0\0\0",
  "8\0\0\0\0\0\0\0",
  "9\0\0\0\0\0\0\0",
  "10\0\0\0\0\0\0",
  "11\0\0\0\0\0\0",
  "12\0\0\0\0\0\0"
 },
      *KisesStr[4]={
  "q\0\0\0\0\0\0\0",
  "w\0\0\0\0\0\0\0",
  "e\0\0\0\0\0\0\0",
  "r\0\0\0\0\0\0\0"
 },
     *NechtoStr[2]={
  "\0\0\0\0\0\0\0\0",
  "\0\0\0\0\0\0\0\0"
 };
 MODE **TigersPtr=&TigersStr[0], **KisesPtr=&KisesStr[0],**NechtoPtr=&NechtoStr[0];
 STRLIST List[]={
  {7,21, 8,21, "Количество $т$игров", 8, LIGHTGRAY,MAGENTA, 
   12, TigersPtr, ALT_N},
  {10,21, 11,21, "Количество $к$ис", 8, WHITE,CYAN,
   4, KisesPtr, ALT_R},
   {10,45, 11,45, "Количество $н$ечто", 9, WHITE,BROWN, 
   2, NechtoPtr, ALT_Y}
 };
 GetText (&Menu,List);
}

void Test_Mes (void) {
 WINDOW Win={ 9,20,17,60, " Помощь ",
  " $Лозинский$ тебе поможет, на хрен тебя,\n"
  " понимаешь.", BLACK, YELLOW, CYAN, 0, YES, 0 };
 MessageText (&Win);
}

void Test_Popup (void) {
 MENU Menu={5,3,10,14, 4, "1!1!2@2\"3#3:4$4$", LIGHTGRAY, WHITE, BLUE, 0,
  YES, 0};
 MENUITEM Item[]={
  {"Пункт 3.$1$",YES,Nop},
  {"Пункт 3.$2$",YES,Nop},
  {"Пункт 3.$3$",YES,Nop},
  {"Пункт 3.$4$",YES,Nop}
 };
 Popup (&Menu,Item);
}

void Test_List(void) {
 MENU Menu={4,3,7,18, 2, "цЦwWтТnN", DARKGRAY,LIGHTGREEN,CYAN, 0,
  YES,0};
 MENUITEM Item[]={
  {"$Ц$вета",    YES,Nop},
  {"$Т$игры",YES,Nop}
 };
 MODE *Colors[]={"Белый","Красный","Синий","Зеленый"};
 MODE *Tigers[]={"Злой","Добрый"};
 MODE **ColorsPtr=&Colors[0], **TigersPtr=&Tigers[0];
 LIST List[]={
  { 0x04, 0x00, ColorsPtr },
  { 0x02, 0x01, TigersPtr }
 };
 ListSwitcher (&Menu,Item,List); 
}

void Test_Swit(void) {
 MENU Menu={3,3,7,16, 3, "pPзЗfFаАvVмМ", DARKGRAY,LIGHTGREEN,CYAN, 0,
  YES,0};
 MENUITEM Item[]={
  {"$З$вук",    YES,Nop},
  {"З$а$ставка",YES,Nop},
  {"\"$М$ышь\"",YES,Nop}
 };
 Switcher (&Menu,Item); 
}

void Work(void) {
 // Popup colors: LIGHTGRAY, WHITE, BLUE
 MENU Menu={1,1,6,10, 4, "1!1!2@2\"3#3:4$4$", LIGHTBLUE, WHITE, LIGHTGRAY, 0,
  YES,0};
 MENUITEM Item[]={ 
  {"Пункт $1$",YES,Test_Swit},
  {"Пункт $2$",YES,Test_List},
  {"Пункт $3$",YES,Test_Popup},
  {"Пункт $4$",YES,Test_Text}
 };
 PullDown (&Menu,Item);
}

void Helps(void) {
 MENU Menu={1,10,5,20, 3, "gGпП\xFF\xFF\xFF\xFF[{хХ", LIGHTBLUE, WHITE, LIGHTGRAY, 0,
  YES,0};
 MENUITEM Item[]={
  {"$П$омогите!",YES, Test_Mes},
  {"\0",NO,Nop},//Разделитель
  {"$Х$а-ха",NO,Nop}
 };
 PullDown (&Menu,Item);
}

void Go_Out (void) {
 Set_Key (2,ESC,START);
 Ext_Function=Close_System;
}

void Out (void) {
 MENU Menu={1,19,3,30, 1, "gGпП", LIGHTBLUE, WHITE, LIGHTGRAY, 0,
  YES,0};
 MENUITEM Item[]={
  {"$П$усто",YES,Close_System}
 };
 PullDown (&Menu,Item);
}

//extern unsigned _stklen = 8000 ;

*/

/*
MODE *MasksStr[6]={
  "\0\0\0\0\0\0\0\0\0\0\0\0\0",
  "\0\0\0\0\0\0\0\0\0\0\0\0\0",
  "\0\0\0\0\0\0\0\0\0\0\0\0\0",
  "\0\0\0\0\0\0\0\0\0\0\0\0\0",
  "\0\0\0\0\0\0\0\0\0\0\0\0\0",
  "\0\0\0\0\0\0\0\0\0\0\0\0\0"};

void Select_Filer (void) {
 MODE c;
 MODE Name[80];
 MENU Menu={4,20,11,44, 1, "  Окно выбора файлов:",
  WHITE,LIGHTRED,BLUE, 0, YES,YES};
 static MODE **MasksPtr=&MasksStr[0];
 STRLIST List[]={
  {6,25, 7,25, "Введите $м$аску:", 12, WHITE,CYAN,
   6, MasksPtr, ALT_V}
 };
 Name[0]='\0';
 while (1) {
  c=GetText (&Menu,List);
  if (c==NO || MasksStr[0][0]=='\0') return;
  else {
   c=Filer (MasksStr[0], Name, 0x01);
   if (c==0x00) { //Выбран файл
   }
   else if (c==0xff) { //ESC
   }
   else { //Ошибка!
   }
  }
 }
}

void Go_Filer (void) {
 Set_Key (2,ESC,START);
 Ext_Function=Select_Filer;
}

void main (int argc,char *argv[]) {

 MENU Hor= {0,0,0,79, 3, "hHрРgGпПdDвВ", LIGHTBLUE, WHITE, LIGHTGRAY, 0, NO, 0};
 MENUITEM Hors[]={
  {" $Р$абота",YES, Work},
  {" $П$омощь",YES, Helps},
  {" $В$ыход",YES, Out}
 };
 MENU Quit= { 9,27,15,53, NO,
" Вы действительно хотите\n"
"$закончить$ работу и выйти\n"
"        в $DOS$ ?", DARKGRAY, WHITE, GREEN, 0 ,YES, 0 };

 if (!textmode()) return;

// extern void TableTest(void);     //!!!
// TableTest();                     //!!!



 Mouse=Init_Mouse ();
 InitPath (argv[0]);
 HelpFlag=OpenHelp();
 Select_Filer ();                 //!!!

AGAIN:
 Clr_Scr();
// HorMenu (&Hor,Hors);
// if (AnswMenu (&Quit)) Close_System ();
// else goto AGAIN;
}
*/