Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: 6308
Последний выпуск: 19.06.2015

C для профессиональных программистов

                               ГЛАВА 2                         -- 1 --
                               -------

                           ВСПЛЫВАЮЩИЕ ОКНА
     -----------------------------------------------------------------

          Всплывающие окна   могут   придать   вашей   программе   тот
     профессиональный вид,  который не может  быть  достигнут  другими
     средствами.  Всплывающие  окна создают впечатление,  что вы,  как
     программист,  в  совершенстве  владеете  экраном.   А   так   как
     пользователь  обычно  судит  о  программе по ее пользовательскому
     интерфейсу, то это положительное впечатление распространится и на
     всю программу в целом.

          Данная глава  содержит  описание  полного набора функций для
     всплывающих окон,  которые позволят вам создавать и  использовать
     множественные окна.

          Программы управления   окнами   используют  функции  прямого
     доступа к видеопамяти,  представленные в Главе 1. Из-за того, что
     окна в большинстве случаев, имеют значительно больший размер, чем
     меню,  то использование функций из ROM-BIOS просто невозможно,  -
     даже на самых быстрых компьютерах.

          Однако перед   рассмотрением  оконных  функций  очень  важно
     правильно понять,  что  же  такое  всплывающие  окна  и  как  они
     используются.






                                 
Глава II                                                       -- 2 --


                          Теория всплывающих окон.
     -----------------------------------------------------------------

          Всплывающее окно   представляет    собой    часть    экрана,
     используемую для специальных целей. Перед появлением окна текущее
     содержимое экрана сохраняется и  лишь  после  этого  производится
     отображение окна.

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

          Хотя это  и  не  обязательно,  но  все  хорошие   программы,
     работающие  с  окнами,  позволяют интерактивно изменять размеры и
     позицию окна на экране. Следовательно, оконные функции допускают,
     что  окно  не  всегда  будет  находиться в одном и том же месте и
     иметь один и тот же размер.

          Разработка функций,  управляющих  окнами,  является  сложной
     задачей  из-за  необходимости  обеспечения запрета для прикладной
     программы осуществлять вывод за границы окна.  Поскольку  размеры
     окна   могут   изменяться  без  "сообщения"  об  этом  прикладной
     программе,  то именно функции управления окнами,  а не прикладная
     программа,    должны   предохранить   от   выхода   за   границы.
     Следовательно,  все обычные функции Си, осуществляющие ввод/вывод
     на  консоль  (например,  printf()  и  lets()  )  ,  не могут быть
     использованы и должны быть заменены на  функции,  ориентированные
     на ввод/вывод с использованием окон.

          Теория использования  окон  крайне проста.  Каждая отдельная
     задача программы использует свое собственное  окно.  При  запуске
     задачи активируется и ее окно.  При завершении работы задачи - ее
     окно удаляется.  Если же задача прерывается,  то,  хотя ее работа
     приостанавливается,  но  ее  окно  не  удаляется,  а инициируемая
     прерыванием задача,  просто создает свое окно поверх предыдущего.
     (Обычно те задачи, которые не используют окон, очищают экран. Это
     приводит к рассеиванию внимания пользователя.  В то же время  при
     использовании  окон  подобные  прерывания  выглядят как временные
     паузы).

          Чтобы понять,  как  окна  могут  быть  наиболее   эффективно
     использованы, предположим, что вы разработали текстовый редактор,
     включающий  ряд  дополнительных  функций,  таких  как   "записная
     книжка",  калькулятор  с  четырьмя  математическими  операциями и
     конвертер чисел из десятичного в шестнадцатиричное представление.
     Так  как  все  эти  функции  в  действительности  не  относятся к
     операциям  редактирования  текста,   то   их   реализация   тесно
     переплетается   с  концепцией  всплывающих  окон.  Таким  образом
     получается,  что  использование  какой-либо  из   вспомогательных
     функций лишь приостанавливает основную задачу (редактирование), а
     не прерывает ее.




                                 
Глава II                                                       -- 3 --



               Оконные структуры.
     -----------------------------------------------------------------

          Правильная реализация  всплывающих  окон требует,  чтобы все
     атрибуты,  необходимые для описания их границ, были в любое время
     доступны  всем  оконным  функциям.  Для достижения этого мы будем
     использовать  концепцию  структуры,  аналогичную   той,   которая
     использовалась  при  описании  функций спускающихся меню.  Однако
     структура окна содержит некоторую специфическую  информацию. Ниже
     показан массив, используемый для хранения структур.

                  struct window_frame
                    int startx, endx, starty, endy; /*позиция окна*/
                    int curx, cury; /*текущая позиция курсора в окне*/
                    unsigned chsr *p; /*указатель буфера*/
                    char *header; /*имя окна*/
                    int border; /*включение/выключение границ*/
                    int active; /*на экране или невидимо*/
                  } frame [MAX_FRAME];

          Переменные startx,  starty,  endx и endy  хранят  координаты
     верхнего  левого  и  нижнего правого углов окна.  Текущая позиция
     курсора в окне содержится в переменных curx  и  cury.  Сохранение
     этих переменных осуществляется из-за того,  что положение курсора
     может изменяться и вручную и путем использования оконных функций.
     Указатель p указывает на область памяти,  хранящей первоначальное
     содержимое  части  экрана,  занятой  данным  окном.  Часто   окно
     снабжается  заголовком,  иденцифирующим содержимое окна.  На этот
     заголовок и указывает header.  Переменная border используется для
     определения   необходимости   вычерчивания  границ  вокруг  окна.
     Переменная active установлена в "1", если в данный момент окно на
     экране, и в "0" - в противном случае.

          С точки   зрения   программирования  использование  окон  не
     составляет труда.  Во-первых,  вы создаете структуру окна, затем,
     когда  появляется  необходимость в окне,  то при записи в него вы
     используете    специальные     оконно-ориентированные     функции
     ввода/вывода.  Когда  же  окно больше не нужно,  вы деактивируете
     его.



                                 
Глава II                                                       -- 4 --


                         Создание структуры окна.
     -----------------------------------------------------------------

          Ниже показана    функция    под   названием   make_window(),
     используемая для создания структуры окна.


         /* Создать рамку спускающегося окна.
          Возвратить 1, если рамка окна  может быть создана
          и 0 в противном случае */
         маке_window(num, header,startx,starty,endx,endy,border)
         int num; /* номер окна */
         char *header; /* текст заголовка */
         int startx,starty; /* координаты X,Y левого верхнего угла */
         int endx,endy; /* координаты X,Y правого верхнего угла */
         int border; /* без бордюра, если 0 */
         {
           unsigned char *p;

           if(num>MAX_FRAME) {
             printf("Too many windows\n");
             return 0;
           }

          if((startx>24) || (startx<0) || (starty>78) || (starty<0)) {
             printf("range error");
             return 0;
           }

           if((endx>24) || (endy>79)) {
             printf("window won't fit");
             return 0;
           }

       /* отвести достаточное количество памяти */
       p=(unsigned char *) malloc(2*(endx-startx+1)*(endy-starty+1));
       if(!p) exit(1); /* перейти к высшему собственному обработчику
       ошибок */

           /* создать рамку */
           frame[num].startx = startx; frame[num].endx = endx;
           frame[num].starty = starty; frame[num].endy = endy;
           frame[num].p = p;
           frame[num].haeder = header;
           frame[num].border = border;
           frame[num].active = 0;
           frame[num].curx = 0; frame[num].cury = 0;
           return 1;
         }

          Как вы  можете  видеть  из  описания  функции,  она  требует
     передачи  номера  окна,  структуру которого вы хотите создать,  и
     всей остальной соответствующей информации.  Ниже  показан  пример
     вызова  make_window()  для  создания  окна  номер  0 с заголовком


                                 
Глава II                                                       -- 5 --


     "Редактор [Esс для выхода]",  с верхним левым углом  в  0,0  и  с
     нижним правым углом в 24,78 и имеющем границы.

      make_window (0, "Редактор [Ese для вывода]", 0,0,24,78, BORDER);

          Отметим, что переменные, определяющие позицию курсора curx и
     cury устанавливаются 0.  Это означает,  что при первой  активации
     окна,  курсор будет установлен в его верхний левый угол.  Функция
     также управляет размещением окна на экране.








                                 
Глава II                                                       -- 6 --


                      Активирование и деактивирование окна.
     -----------------------------------------------------------------

          Для активирования окна используется функция window (). Здесь
     num  будет  содержать  номер  структуры  окна,  которое вы хотите
     использовать.


         /* Вывести на экран спускающееся окно */
         void window(num)
         int num; /* номер окна */
         {
           int vmode, choice;
           int x, y;

           vmode = video_mode();
           if((vmode!=2) && (vmode!=3) && (vmode!=7)) {
             printf("video must be in 80 column text mode");
             exit(1);
           }
           /* установить соответствующий адрес видеопамяти */
           if(vmode==7) vid_mem = (char far *) 0xB0000000;
           else vid_mem = (char far *) 0xB0000000;

           /* сделать окно активным */
           if(!frame[num].active) { /* используется не постоянно */
           save_video(num); /* сохранить текущее содержимое экрана */
           frame[num].active = 1; /* установить флаг активности */
           }

           if(!frame[num].border) draw border(num);
           display_header(num); /* вывести окно на экран */

           x = frame[num].startx + frame[num].curx + 1;
           y = frame[num].starty + frame[num].cury + 1;
           goto_xy(x,y);
         }


          Как вы можете видеть, данная функция очень похожа на функцию
     menu(),  показанную  в  предыдущей  главе.   Переменная   vid-mem
     является глобальным указателем типа char far.

          Функция display_header(),  показанная ниже, используется для
     отображения в центре верхней границы окна его заголовка.  Если же
     заголовок не помещается в эту строку, то он не выводится.


         /* вывести текст заголовка начиная с определенной
         позиции */
         void display_header(num)
         int num;
         {
           register int y,len;


                                 
Глава II                                                       -- 7 --



           y = frame[num].starty;
           /* Вычислить начальную позицию относительно центра текста
           заголовка, если отрицательная, то текст не подходит */
           len = strlen(frame[num].header);
           len = (frame[num].endy - y - len) / 2;
           if(len<0) return; /* не выводить на экран */
           y = y +len;

           write_string(frame[num].startx, y,
                        frame[num].header,NORM_VID);
         }


          Если вы  хотите,  чтобы  заголовок  выводился  в   инверсном
     изображении,  то  вместо NORМ_VID (имеющем значение 7) подставьте
     REV_VID (со значением 70Н).

          Для деактивации окна используется  показанная  ниже  функция
     deactivate(), которой передается номер удаляемого окна.


         /* Деактивировать окно и удалить его с экрана */
         deactivate(num)
         int num;
         {
           /* установить курсор в левый верхний угол */
           frame[num].curx = 0;
           frame[num].cury = 0;
           restore_video(num);
         }


          Как вы видите,  функция устанавливает позицию курсора в 0,0.
     Однако,  поскольку вам могут встретиться  некоторые  ситуации,  в
     которых  более  желательно  не  устанавливать  курсор  в  нулевую
     позицию,  то  вы  можете   изменить   поведение   этой   функции,
     сообразуясь со своими намерениями.





                                 
Глава II                                                       -- 8 --


               Оконные функции ввода/вывода.
     -----------------------------------------------------------------

          Перед использованием     окна     необходимо     разработать
     значительное   число  консольных  оконно-ориентированных  функций
     ввода/вывода.  Чтобы понять,  почему требуется так много функций,
     вспомните  о  том,  сколько  консольных  функций  ввода/вывода  в
     стандартной  библиотеке  Си.  Функции,  представленные  в  данной
     главе,  в  действительности  составляют  лишь  минимальный набор,
     необходимый для использования окон.  Хотя они не включают в  себя
     ориентированные  на  использование  окон  версии  всех консольных
     Си-функций, но все же имеют большой объем. Как вы убедитесь, даже
     простейшие  операции,  такие  как чтение символа с клавиатуры или
     вывод его на экран,  реализуются  программами  большого  объеема,
     поскольку  необходимо  отслеживать  и  сохранять  текущую позицию
     курсора и не допускать выхода за границы окна.  Помните,  что для
     манипулирования  экраном  нельзя использовать никаких стандартных
     возможностей,  предоставляемых DOS.  Например,  при необходимости
     выполнить    "возврат    каретки"   это   должно   быть   сделано
     самостоятельно,  внутри функции;  нельзя просто вызвать  DOS  для
     вывода соответствующей последовательности.

          Для облегчения    идентификации    все    оконные    функции
     ввода/вывода начинаются со слова  window.  Кроме  того,  все  эти
     функции в качестве своего первого аргумента принимают номер окна,
     к которому осуществляется доступ.




                                 
Глава II                                                       -- 9 --


                  Функция позиционирования курсора в окне.
     -----------------------------------------------------------------

          Возможно, кому-то это покажется странным, но первая функция,
     в которой возникает потребность,  это оконный эквивалент  функции
     goto_xy().  Объяснение  этому  очень  простое.  Поскольку оконные
     функции  ввода/вывода   должны   самостоятельно   позиционировать
     курсор, то должен существовать некоторый способ установки курсора
     в  нужную  позицию.  Функция  window_xy(),  представленная  ниже,
     делает  именно  это.  (Для представленных здесь программ работы с
     окнами позиция 0,0 соответствует левому верхнему углу окна).


         /* Установить курсор в определенную позицию окна.
            Возвратить 0 при выходе за границу и не ноль -
            в противном случае */

         window_xy(num, x, y,)
         int num, x, y;
         {
           if(x<0 || x+frame[num].startx>=frame[num].endx-1)
             return 0;
           if(x<0 || y+frame[num].starty>=frame[num].endy-1)
             return 0;
           frame[num].curx = x;
           frame[num].cury = y;
           goto_xy(frame[num].startx+x+1, frame[num].starty+y+1);
           return 1;
           }


          Ключом к  пониманию функции window_xy() является напоминание
     о  том,  что  значения  координат  X,Y   внутри   окна   остаются
     неизменными,  независимо от расположения окна на экране.  То есть
     координаты X,Y вычисляются относительно окна,  а не  относительно
     экрана.  Другими  словами,  если  вы  установите  курсор в окне в
     позицию с координатами 2,2,  то он  всегда  будет  находиться  во
     второй   строке  сверху  и  второй  позиции  относительно  левого
     верхнего угла окна, независимо от положения окна на экране.

          Фактически функция  window_xy()  производит   преобразование
     координат   курсора   относительно  окна  в  реальные  координаты
     относительно  экрана.  Кроме  этого,   функция   window_xy()   не
     позволяет курсору выйти за пределы окна.                                 
Глава II                                                      -- 10 --


                  Функция window_getche()
     -----------------------------------------------------------------

          При  работе  с  окнами  вы  не  можете  использовать функцию
     getche(), которая  считывает  введенный  с  клавиатуры  символ  и
     отображает его на экране,  поскольку это может привести к  выходу
     за пределы окна.  Поэтому должна быть использована альтернативная
     оконная функция window_getche().  Эта функция  считывает  символ,
     который вводится в текущую позицию окна.


        /*  Ввести  с  клавиатуры  символ  в   окно.   Возвратить
            полный 16-разрядный скан-код. */
         window_getche(num)
         int num;
         {
           union inkey {
             char ch[2];
             int i;
           } c;

           if(!frame[num].active) return 0; /* окно не активное */

           window_xy(num, frame[num].curx, frame[num].cury);

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

           if(c.ch[0]) {
            switch(c.ch[0]) {
             case '\r':  /* нажата клавиша ENTER */
              break;
             case BKSP; /* возврат */
              break;
             default:
           if(frame[num].cury+frame[num].starty < frame[num].endy-1) {
             write_char(frame[num].startx = frame[num].curx=1,
               frame[num].starty+frame[num].cury+1,c.ch[0],NORM_VID);
                 frame[num].cury++;
               }
             }
             if(frame[num].curx < 0) frame[num].curx = 0;
             if(frame[num].curx+frame[num].startx > frame[num].endx-2)
               frame[num].curx--;
             window_xy(num, frame[num].curx, frame[num].cury);
           }
           return c.i;
         }

          В отличие   от  функции  getche(),  функция  window_getche()
     возвращает полный 16-разрядный скан-код.  Это  означает,  что  вы
     имеете  доступ  как к стандартным кодам символов в младших восьми
     разрядах,  так и к позиционным кодам символов  в  старших  восьми
     разрядах. Если вам не нужны позиционные коды, то вы можете просто
     назначить   возвращаемое   функцией   window_getche()    значение


                                 
Глава II                                                      -- 11 --


     символьной переменной.

          Функция работает  следующим  образом.  Если окно не является
     активным  (т.е.  его  нет  на  экране),  функция  возвращает   0.
     Поскольку  код  0 не может соответствовать символу,  введенному с
     клавиатуры,  то ваша программа сможет  обнаружить  эту  ситуацию.
     Затем  курсор  устанавливается  в свою текущую позицию в окне,  и
     считывается код нажатой клавиши.  Если это обычная клавиша,  а не
     клавиша  типа  RETURN  или  BACKSPASE,  инкрементируется  текущее
     значение  переменной  Y,  соответствующее  положению  курсора,  и
     соответствующий символ выводится на экран.  Если курсор находится
     на границе окна, значение Y декрементируется. Последнее обращение
     к  функции  window_xy() используется для того,  чтобы переместить
     курсор в следующую позицию экрана.

          Функция window_getche не позволит  вам  вводить  символы  за
     границей  окна.  Следует  помнить,  что  все  окна имеют границы,
     которые могут и не отображаться явно в виде линий.  Если  граница
     не  отображается,  это  значит,  что  для  этой цели используется
     символ пробела.  Это имеет смысл,  если окно отображается на фоне
     изображения,  имеющего  другой  цвет.  Даже  если  граница  и  не
     обозначена явно,  то символы все равно не могут  отображаться  за
     пределами окна.

          Как утверждается в разделе 1,  функция bios_key() cпецифична
     для Турбо Си.  Если вы используете другой компилятор  Си,  то  вы
     можете  использовать версию функции bios_key(),  представленную в
     главе 1.


                                 
Глава II                                                      -- 12 --


               Функция window_gets()
     -----------------------------------------------------------------

          Для чтения  строки,  введенной  в  окно,  можно использовать
     представленную  ниже  функцию   window_gets().   Она   не   такая
     изощренная, как большинство функций gets(),  но может служить для
     многих  целей.  Вы  всегда  можете  расширить  ее  функциональные
     возможности, если пожелаете.


         /* Считать строку из окна */
         void window_gets(nums, s)
         int num;
         char *s;
         {

           char ch, *temp;

           temp = s;
           for(;;) {
             ch = window_getche(num);
             switch(ch) {
               case '\r': /* нажата клавиша ENTER */
               *s='\0';
               return;
             case BKSP:   /* возврат */
               if(s>temp) {
                 s--;
                 frame[num].cury--;
                 if(frame[num].cury<0) frame[num].cury = 0;
                 window_xy(num, frame[num].curx, frame[num].cury);
                 write_char(frame[num].startx+ frame[num].curx+1'
                   frame[num].starty+frame[num].cury+1, ' ',NORM_VID);
               }
               break;
             default: *s = ch;
               s++;
             }
           }
         }


          При нажатии клавиши ВАСКSPASE,  необходимо вернуть курсор на
     одну позицию влево,  стереть записанный там символ и на его место
     записать пробел.











                                 
Глава II                                                      -- 13 --


               Функция window_putchar()
     -----------------------------------------------------------------

          При выводе символа в окно необходимо проверять,  является ли
     окно  активным  и  не  выходит  ли символ за границу окна.  После
     вывода символа курсор продвигается на одну позицию. Выполняет эти
     действия представленная ниже функция window_putchar().


         /* Вывести символ в текущую позицию курсора в созданном окне.
         Возвратить  0, если окно не активное, и 1 - в противном
         случае */
         window_putchar(num, ch)
         int num;
         char ch;
         {
           register int x, y;
           char far *v;

           /* убедиться, что окно активное */
           if(!frame[num].active) return 0;

           x = frame[num].curx = frame[num].startx + 1;
           y = frame[num].cury = frame[num].starty + 1;

           v = vid_mem;
           v += (x*160) + y*2;  /* вычисляется адрес */
           if(y>=frame[num].endy) {
             return 1;
           }
           if(x>=frame[num].endx) {
             return 1;
           }

           if(ch=='\n') {  /* символ перехода на следующую строку */
             x++;
             y = frame[num].startx+1;
             v = vid_mem;
             v += (x*160) = y*2;  /* вычислить адрес */
             frame[num].curx++;   /* нарастить x */
             frame[num].cury = 0; /* нарастить y */
           }
           else {
             frame[num].cury++;
             *v++ = ch;           /* вывести символ */
             *v++ =NORM_VID;      /* нормальные атрибуты символа */
           }
           window_xy(num, frame[num].curx, frame[num].cury);
           return 1;
         }

          Эта функция  не занимается обнаружением ошибочнных ситуаций,
     когда символ выходит за границу окна.  Смысл этого состоит в том,
     что  размер  окна  может  динамически  изменяться и то сообщение,


                                 
Глава II                                                      -- 14 --


     которое помещалось в окно в следующий  момент  времени  может  не
     помещаться.  В  этом  случае  функция  просто  не  отображает  те
     символы, которые выходят за границу окна.

          Обратите внимание,  что  нажатие  клавиши  возврата  каретки
     требует  перевода  курсора  к левой границе окна и на одну строку
     вниз, если это возможно.










                                 
Глава II                                                      -- 15 --


               Функция window_puts
     -----------------------------------------------------------------

          Функция window_puts выводит заданную строку в активное окно,
     используя при этом функцю window_putchar().


      /* Вывести строку, начиная с текущей позиции курсора в окне.
                                                         /* 60 */
         Возвратить 0, если окно не активное и 1 в противном случае */

         window_puts(num, str)
         int num;
         char *str;
         {

           /* убедиться, что окно активное */
           if(!frame[num].active) return 0;

           for( ; *str; str++)
             window_putchar(num, *str);
           return 1;
         }







                                 
Глава II                                                      -- 16 --


               Дополнительные функции управления экраном.
     -----------------------------------------------------------------

          При работе  с  окнами  также  используются следующие функции
     управления экраном:

          Функция             Назначение
         ---------            ------------
        window cls()          очищает окно
        window cleol()        очищает часть окна от текущей позиции
                              до конца строки
        window_upline()       перемещает курсор на одну строку вверх
        window_downline()     перемещает курсор на одну строку вниз
        window_bksp()         перемещает курсор на одну позицию влево

          Эти функции  представлены  ниже.  Следуя  используемым в них
     общим  принципам,  вы  можете  создать  свои  функции  управления
     экраном.


         /* Очистить окно */
         void window_cls(num)
         int num;
         {

           register int i,j;
           char far *v, far *t;

           v = vid_mem;
           t = v;
           for(i=frame[num].starty+1; i<frame[num].endy; i++)
             for(j=frame[num].startx+1; j<frame[num].endx; j++) {
               v = t;
               v += (j*160) + i*2;
               *v++ = ' ';     /* вывести пробел */
               *v =  NORM_VID; /* нормальные видеоатрибуты */
             }
             frame[num].curx = 0;
             frame[num].cury = 0;
           }

           /* очистить до конца строки */
           void window_cleol(num)
           int num;
           {
             register int i, x, y;

             x = frame[num].curx;
             y = frame[num].cury;
             window_xy(num, frame[num].curx, frame[num].cury);

             for(i=frame[num].cury; i<frame[num].endy-1; i++)
               window_putchar(num,' ');
             window_xy(num, x, y);


                                 
Глава II                                                      -- 17 --


           }

     /* Переместить курсор на одну строку вверх. Возвратить ненулевой
        код в случае успеха и 0 - в противном случае */
           window_upline(num)
           int num;
           {
             if(frame[num].curx>0) {
               frame[num].curx--;
               window_xy(num, frame[num].curx, frame[num].cury);
               return 1;
             }
             return 0;
           }

           window_downline(num)
           int num;
           {
             if(frame[num].curx<frame[num].endx-frame[num].startx-1) {
               frame[num].curx++;
               window_xy(num, frame[num].curx, frame[num].cury);
               return 1;
             }
             return 1;
           }

           /* стереть предыдущий символ */
           window_bksp(num)
           int num;
           {
             if(frame[num].cury>0) {
             frame[num].cury--;
             window_xy(num, frame[num].curx, frame[num].cury);
             window_putchar(num, ' ');
             frame[num].cury--;
             window_xy(num, frame[num].curx, frame[num].cury);
           }
         }





                                 
Глава II                                                      -- 18 --


            Изменение размера и положения  окна  во  время
            выполнения  программы
     -----------------------------------------------------------------

          Хотя функция  make_window()  и  используется  для  установки
     начальных  размеров  и  положения  окна  на  экране,  однако  эти
     параметры   могут  динамически  изменяться  во  время  выполнения
     программы.   Изменение   одного   или   более   параметров   окна
     производится по командам,  поступающим от пользователя.  При этом
     текущее  окно  уничтожается   и   воссоздается   уже   с   новыми
     параметрами.  Представленные  ниже  программы size() и move() как
     раз и используются для изменения размеров и положения  окна.  Для
     изменения   формы   и  положения  окна  используются  клавиши  со
     стрелками, а также клавиши HOME, END, PGDN и PGUP.

         /* Интерактивное изменение размера окна */
         void size(num)
         int num;
         {
           char ch;
           int x, y, startx, starty;

           /* активировать, если необходимо */
           if(!frame[num].active) window(num);
           startx = x = frame[num].startx;
           starty = y = frame[num].starty;
           window_xy(num, 0, 0);

           do {
             ch = get_special();
             switch(ch) {
               case 75:     /* влево */
                 starty--;
                 break;
               case 77:     /* вправо */
                 starty++;
                 break;
               case 72:     /* вверх */
                 startx--;
                 break;
               case 80:     /* вниз */
                 startx++;
                 break;
               case 71:     /* влево вверх */
                 startx--;starty--;
                 break;
               case 73:     /* вправо вверх */
                 startx--;starty++;
                 break;
               case 79:     /* влево вниз */
                 startx++;starty--;
                 break;
               case 81:     /* вправо вниз */
                 startx++;starty++;


                                 
Глава II                                                      -- 19 --


                 break;
               case 60:
                    /* F2: отменить и вернуться к исходному размеру */
                 startx = x;
                 starty = y;
                 ch = 59;
             }

             /* смотри при выходе за диапазон */
             if(startx<0) startx++;
             if(startx>=frame[num].endx) startx--;
             if(starty<0) starty++;
             if(starty>=frame[num].endy) starty--;
             deactivate(num);  /* стереть окно старого размера */
             frame[num].startx = startx;
             frame[num].starty = starty;
             window(num);      /* вывести окно с новым размером */
           } while(ch!=59);  /* F1 для подтверждения нового размера */
           deactivate(num);
         }


         /* Интерактивное перемещение окна */
         void move(num)
         int num;
         {
           char ch;
           int x, y, ex, ey, startx, starty, endx, endy;

           /* активировать, если необходимо */
           if(!frame[num].active) window(num);
           startx = x = frame[num].startx;
           starty = y = frame[num].starty;
           endx = ex = frame[num].endx;
           endy = ey = frame[num].endy;
           window_xy(num, 0, 0);

           do {
             ch = get_special();
             switch(ch) {
               case 75:     /* влево */
                 starty--;
                 endy--;
                 break;
               case 77:     /* вправо */
                 starty++;
                 endy++;
                 break;
               case 72:     /* вверх */
                 startx--;
                 endx--;
                 break;
               case 80:     /* вниз */
                 startx++;


                                 
Глава II                                                      -- 20 --


                 endx++;
                 break;
               case 71:     /* влево вверх */
                 startx--;starty--;
                 endx--;endy--;
                 break;
               case 73:     /* вправо вверх */
                 startx--;starty++;
                 endx--;endy++;
                 break;
               case 79:     /* влево вниз */
                 startx++;starty--;
                 endx++;endy--;
                 break;
               case 81:     /* вправо вниз */
                 startx++;starty++;
                 endx++;endy++;
                 break;
       case 60:     /* F2: отменить и вернуться к исходному размеру */
                 startx = x;
                 starty = y;
                 endx = ex;
                 endy = ey;
                 ch = 59;
             }

             /* смотри при выходе за диапазоном */
             if(startx<0) {
               startx++;
               endx++;
             }
             if(endx>=25) {
               startx--;
               endx--;
             }
             if(starty<0) {
               starty++;
               endy++;
             }
             if(endy>=79) {
               starty--;
               endx--;
             }
             /* стереть окно в старой позиции */
             deactivate(num);
             frame[num].startx = startx;
             frame[num].starty = starty;
             frame[num].endx = endx;
             frame[num].endy = endy;
             /* вывести окно в новую позицию */
             window(num);
             } while(ch!=59);  /* F1 для подьверждения изменения */
           deactivate(num);
         }


                                 
Глава II                                                      -- 21 --



          При использовании как функции size(),  так и функции  move()
     после  завершения  изменения  параметров окна нажмите клавишу F1.
     Окно будет иметь установленные размеры  и  положение  при  каждой
     последующей активации и до тех пор, пока вы не измените их снова.
     Для прерывания выполнения обеих функций используется  клавиша F2,
     при этом окно сохраняет старые значения размеров и положения. При
     использовании этих функций  допускается,  чтобы  окна,  в  момент
     изменения их размеров или положения необязательно были активными.








                                 
Глава II                                                      -- 22 --


          Создание прикладных программ, использующих всплывающие окна
     -----------------------------------------------------------------

          Работая с окнами очень важно помнить,  что при  вводе-выводе
     должны  использоваться специальные оконные функции. Использование
     для этих целей стандартных  функций  Си  чревато  неприятностями,
     потому  что  создает возможность нарушения границы окна.  Оконной
     функции,  аналогичной по выполняемым действиям  функции  prinf(),
     разработано не было,  и вы,  возможно,  захотите создать для этих
     целей свою собственную функцию.  Но простейший  способ  вывода  в
     окно данных, тип которых отличен от символов и строк, заключается
     в   использовании   стандартной    Си-функции    sprintf()    для
     преобразования любых типов данных в строку определенного формата,
     а затем в выводе этой строки в окно с помощью функции window_puts
     ().  Аналогичный  способ  позволяет  вводить данные,  отличные от
     символов  и  строк.  При  этом  функция  window_gets()  считывает
     данные,   а  приводит  их  к  соответствующему  типу  стандартная
     Си-функция sscanf(), которая выполняет преобразование поступающих
     от клавиатуры данных.

          Изображение в   окне   обычно   формируется   другой  частью
     программы,  не той,  которая содержит функции управления  окнами.
     Обычно это делается в функции main() или в функции инициализации,
     которая вызывается в начале программы. Предлагаем вашему вниманию
     три простые программы, использующие окна.





                                 
Глава II                                                      -- 23 --


                 Программа  преобразования  из  десятичной
                 в шестнадцатиричную систему счисления.
     -----------------------------------------------------------------

         /* Десятично-шестнадцатиричный преобразоваель */
         void dectohex()
         {
           char in[80], out[80]
           int n;

           window(1);
           do {
             window_xy(1, 0, 0)  /* перейти к первой строке */
             window_cleol(1);    /* очистить строку */

             window_puts(1, "dec: "); /* промптер */
             window_gets(1, in);      /* считать число */
            window_putchar(1,  '\n'); /* перейти к следующей строке */
             window_cleol(1);          /* очистить ее */
          sscanf(in,"%d", &n); /* преобразовать во внутрений формат */
             sprintf(out, "%s%X", "hex: ",n);  /* преобразовать в
                                  шестнадцатиричное представление */
            window_puts(1, out); /* вывести шестнадцатиричное число */
           } while(*in);
           deactivate(1);
         }

          Функция активирует  свое  окно,  а  затем  в цикле принимает
     десятичные числа и выводит их шестнадцатиричные  эквиваленты,  до
     тех пор,  пока  пользователь  не  нажмет  Ввод  в ответ на запрос
     десятичного  числа.  Перед   возвратом   из   функции   ее   окно
     деактивируется.











                                 
Глава II                                                       -- 24 --


               Калькулятор с четырьмя функциями.
     -----------------------------------------------------------------
          Очень подходящей   и   популярной   областью   использования
     всплывающих   окон   являются   программы   калькуляторов.  Здесь
     представлена программа стекового калькулятора.  Это означает, что
     при работе с ним вы должны сначала вводить операнды, а затем знак
     операции (т.н. постфиксная запись). Операнды помещаются в стек. В
     каждый  момент времени выполняется операция над двумя операндами.
     При  этом  операнды  извлекаются  из  стека,  результат  операции
     отображается и помещается в стек.  Например,  для того, вычислить
     результат выражения (10+5)/5,  вы сначала должны ввести 10, затем
     5, затем знак +. Результат этой операции, число 15, будет выведен
     на дисплей и помещен в вершину стека.  Затем вы вводите 5 и  знак
     /.  Отображается  результат  3.  Стек рассчитан на 100 элементов.
     Можно вводить несколько операндов перед знаком  операции. Функция
     calc(),  а также подпрограммы push() и pop() для работы со стеком
     приводятся ниже.  Хотя эта версия  программы  работает  только  с
     целыми числами,  вы легко можете изменить ее таким образом, чтобы
     она работала с действительными числами.

         #define MAX 100
         int *p;  /* указатель стека */
         int *tos;  /* указатель вершины стека */
         int *bos;  /* указатель дна стека */

     /* стековый, с постфиксной записью калькулятор с четырьмя
        функциями */
         void calc()
         {
           chra in[80], out[80];
           int answer, stack[MAX];
           int a,b;

           p = stack;
           tos = p;
           bos = p+MAX-1;

           window(2);
           do {
              window_xy(2, 0,0);
              window_cleol(2);
              window_puts(2, ": "); /* промптер калькулятора */
              window_gets(2, in);
              window_puts(2, "\n ");
              window_cleol(2);
              switch(*in) {
                case '+':
                  a = pop();
                  b = pop();
                  answer = a+b;
                  push(a+b);
                  break;
                case '-':
                  a = pop();


                                 
Глава II                                                       -- 25 --


                  b = pop();
                  answer = b-a;
                  push(b-a);
                  break;
                case '*':
                  a = pop();
                  b = pop();
                  answer = b*a;
                  push(b*a;
                  break;
                case '/':
                  a = pop();
                  b=pop();
                  if(a==0) {
                      window_putch("divide by 0\n");
                      break;
                    }
                    answer = b/a;
                    break;
                  default:
                    push(atoi(in));
                    continue;
                  }
                  sprintf(out, "%d", answer);
                  window_puts(2, out);
                } while(*in);
                deactivate(2);
              }

            /* Поместить число в стек.  Возвратить 1 в случае успеха и
               0, если стек переполнен */
              push(i)
              int i;
              {
                if(p>bos) return 0;

                *p=i;
                p++;
                return 1;
              }

              /* Извлечь верхний элемент из стека.  Возвратить 0, если
                 стек переполнен */

              pop()
              {
                p--;
                if(p<tos) {
                  p++;
                  return 0;
                }
                return *p;
             }



                                 
Глава II                                                       -- 26 --






                                 
Глава II                                                       -- 27 --


               Всплывающая записная книжка
     -----------------------------------------------------------------

          Другой очень подходящей областью применения всплывающих окон
     являются программы типа "записная книжка". При использовании этих
     программ  у вас может возникнуть потребность внести новую запись.
     Все, что от вас потребуется, это активизировать программу, внести
     новую  запись  и  вернуться  к  тому,  что вы делали ранее.  Ниже
     представлен  пример  очень  простой  программы   типа   "записная
     книжка".


         #include "ctype.h"

         /* Всплывающая записная книжка */
         #define MAX_NOTE 10
         #define BKSP 8
         char notes[MAX NOTE][80];

         void notepad()
         {
           static firs=1;
           register int i, j;
           union inkey {
             char ch[2];
             int i;
           } c;
           char ch;

           /* Инициализировать массив записей, если это необходимо */
           if(frist) {
             for(i=0; i<MAX_note; i++)
               *note[i] = '\0';
             frist = !frist;
           }

           window(3);

           /* вывести на экран существующие записи */
           for(i=0; i<MAX_note; i++)  {
             if(*notes[i]) window_puts(3, notes[i]);
             window_putcar(3, '\n');
           }
           i=0;
           window_xy(3, 0, 0);

           for(;;) {
             c.i = window_getche(3);  /* считать символ, введенный с
                                                         клавиатуры */
             if(tolower(c.ch[1])==59 {  /* по F1 - завершение */
               deactivate(3);
               break;
             }



                                 
Глава II                                                       -- 28 --


             /* если обычный символ, то внести его в запись */
             if(isprint(c.ch[0]) || c.ch[0]==BKSP) {
               window_cleol(3);
               notes[i][0] = c.ch[0];
               j = 1;
               window_putchar(3, notes[i][0]);
               do {
                 ch = window_getche(3);
                 if(ch==BKSP) {
                   if(j>0) {
                     j--;
                     window_bksp(3);
                   }
                 }
                 else {
                   notes[i][j] = ch;
                   j++;
                 }
                 } while(notes[i][j-1]!='\r');
                 notes[i][j-1] = '\0';
                 i++;
                 window_putchar(3, '\n');
               }
               else {             /* это специальная клавиша */
                 switch(c.ch[1]) {
                   case 72:       /* стрелка вверх */
                     if(i>0) {
                       i--;
                       window_upline(3);
                     }
                     break;
                   case 80:       /* стрелка вниз */
                     if(i<MAX_NOTE-1) {
                       i++;
                       window_dowline(3);
                     }
                     break;
                   }
                 }
               }
             }


          Функция notepad()  позволяет  вводить  до  десяти  строк.  С
     помощью клавиш UP ARROW и DOWN ARROW  вы  можите  перемещаться  к
     нужной вам строке.  Старое содержимое строки,  в которую вносится
     новая  запись,  при  этом  стирается.  Для  выхода  из  программы
     "записная книжка" используется клавиша F1.








                                 
Глава II                                                       -- 29 --


               Совместное использование всех программ
     -----------------------------------------------------------------

          В этом разделе приводится программа,  в которой используются
     все    программы   управления   окнами   и   функции   управления
     изображением,  представленные в разделе 1, а также три прикладных
     программы,   использующие   окна.   Программа   имитирует  работу
     редактора  и  позволяет  вам  с  помощью  функциональных   клавиш
     активировать   окна  различного  назначения  или  демонстрировать
     различные особенности  работы  с  окнами.  Вы  можете  немедленно
     ввести эту программу в вашу ЭВМ.


         /* Подпрограмма управления окнами и простая демонстрационная
         программа. Имитируется работа редактора. Три специальные
         оконные утилиты иллюстрируют мощь и очарование программ,
         использующих всплывающие окна. Этими утилитами являются:
         калькулятор с 4-мя функциями, десятично-шестнадцатиричный
         преобразователь и всплывающая записная книжка.    */
         #include "stdio.h"
         #include "dos.h"
         #include "stdlib.h"

         #define BORDER 1
         #define ESC 27
         #define MAC_FRAME 10
         #define REV_VID 0x70
         #define NORM_VID 7
         #define BKSP 8

         void save_video(), restore_video(), pd_driver();
         void goto_xy(, cls(), write_string(), write_char();
         void display_header(), draw_border();
         void window_gets(), size(), move(), window_cls();
         void window_cleol(), window();
         void dectohex(), notepad(), calc();

         char far *vid_mem;

         struct window_frame {
           int startx, endx, starty, endy;
           int curx, cury;
           unsigned char *p;
           char *header;
           iht border;
           int active;
         } frame[MAX_FRAME];


         main()
         {
           union inkey {
           char ch[2];
           int i;


                                 
Глава II                                                       -- 30 --


         } c;
         int i;
         char ch;

         cls();
         goto_xy(0,0);

         /* первым делом, создать рамки окна */
      make_window(0, " Editor [Esc to exit] ", 0, 0, 24, 78, BORDER);
      make_window(1, " Decimal to Hex ", 7, 40, 10, 70, BORDER);
      make_window(2, " Calculator ", 8, 20, 12, 60, BORDER);
      make_window(3, " Notepad [F1 to exit] ", 5, 20, 17, 60, BORDER);

         /* использовать window() для активации описанного окна */
         window(0);
         do {
           c.i = window_getche(0);
           ch = c.i;      /* использовать только младший байт */
           if(ch=='\r')   /* должен выполнять переход к началу
                                            следующей строки */
               window_putchar(0, '\n');

           switch(c.ch[1]) {   /* см. при использовании стрелок или
                                  функциональных клавиш */
             case 59: /* F1 демонстрирует работу функции window() */
               window(1);
               for(i=0; i<10; i++)
                 if(window_xy(1, i, i)) window_putchar(1,'X');
                 getch();
                 deactivate(1);
                 break;
                 case 60: /* F2 демонстрирует изменение размера и
                                                   положения окна */
                 size(1);
                 move(1);
                 break;
               case 61:  /* F3 вызывает калькулятор */
                 calc();
                 break;
               case 62:    /* F4 вызывает десятично-шестнадцатиричный
                               преобразователь */
                 dectohex();
                 break;
               case 63:  /*  F5 вызывает записную книжку */
                 notepad();
                 break;
                 case 72:  /* вверх */
                 window_upline(0);
                 break;
               case 80:    /* вниз */
                 window_downline(0);
                 break;

               }


                                 
Глава II                                                       -- 31 --


             } while (ch!=ESC);
             deactivate(0);  /* удалить окно */
           }


         /***********************************************************/
         /*  Оконные функции                                        */
         /***********************************************************/

         /* Вывести на экран спускающееся окно */
         void window(num)
         int num;  /* номер окна */
         {
           int vmode, choice;
           int x, y;

           vmode = video_mode();
           if((vmode!=2) && (vmode!=3) && (vmode!=7)) {
             printf("video must be in 80 column text mode");
             exit(1);
           }
           /* установить соответствующий адрес видеопамяти */
           if(vmode==7) vid_mem = (char far *) 0xb0000000;
           else vid_mem = (char far *) 0xb0000000;

           if(!frame[num].active) {  /* используется непостоянно */
             save_video(num);/* сохранить текущее содержимое экрана */
             frame[num].active = 1;  /* установить флаг активности */
           }

           if(frame[num].border) draw_border(num);
           dispay_header(num);     /* вывести окно на экран */

           x = frame[num].startx + frame[num].curx + 1;
           y = frame[num].starty + frame[num].cury + 1;
           goto_xy(x, y);

         }


         /* Создать рамку спускающегося окна. Возвратить 1, если рамка
         окна может быть создана и 0 в противном случае */

         make_window(num, header, startx, starty, endx, endy, border)
         int num;         /* номер окна */
         char *header;    /* текст заголовка */
         int startx, starty; /* координаты x,y верхнего левого угла */
         int endx, endy;     /* координаты x,y нижнего правого угла */
         int border;           /* без бордюра, если 0 */
         {
           unsigned char *p;

           if(num>MAX_FRAME) {
             printf("Too many windows\n");


                                 
Глава II                                                       -- 32 --


             return 0;
           }

         if((startx>24) || (startx<0) || (starty>78) || (starty<0)) {
             printf("range error");
             return 0:
           }

           if((endx>24) || (endy>79)) {
             printf("window won't fit");
             return 0;
           }

           /* отвести достаточное количество памяти */
      p = (unsigned char *) malloc(2*(endx-startx+1)*(endy-starty=1));
     if(!p) exit(1);/* используйте ваш собственный обработчик ошибок*/

           /* создать рамку */
           frame[num].startx = startx; frame[num].endx = endx;
           frame[num].starty = starty; frame[num].endy = endy;
           frame[num].p = p;
           frame[num].header = header;
           frame[num].border = border;
           frame[num].active = 0;
           frame[num].curx = 0; frame[num].cury = 0;
           return 1;
         }

         /* редактировать окно и удалить его с экрана */
         deactivate(num)
         int num;
         {

           /* установить курсор в левый верхний угол */
           frame[num].curx = 0;
           frame[num].cury = 0;
           restore_video(num);
         }

         /* Интерактивное изменение размеров окна */
         void size(num)
         int num;
         {
           char ch;
           int x, y, startx, starty;

           /* активировать, если необходимо */
           if(!frame[num].active) window(num);

           startx = x = frame[num].startx;
           starty = y = frame[num].starty;
           window_xy(num, 0, 0);

           do {


                                 
Глава II                                                       -- 33 --


             ch = get_special();
             switch(ch) {
               case 75:    /* влево */
                 starty--;
                 break;
               case 77:    /* вправо */
                 starty++;
                 break;
               case 72:    /* вверх */
                 startx--;
                 break;
               case 80:    /* вниз */
                 startx++;
                 break;
               case 71:    /* влево вверх */
                 startx--; starty--;
                 break;
               case 73:    /* вправо вверх */
                 startx--; starty++;
                 break;
               case 79:    /* влево вниз */
                 startx++; starty--;
                 break;
               case 81:    /* вправо вниз */
                 startx++; starty++;
                 break;
               case 60:    /* F2: отменить и вернуться к исходным
                                                         размерам */
                 startx = x;
                 starty = y;
                 ch = 59;
             }

             if(startx<0) startx++;
             if(startx>=frame[num].endx) startx--;
             if(starty<0) starty++;
             if(starty>=frame[num].endy) starty--;
             deactivate(num);
             frame[num].startx = startx;
             frame[num].starty = starty;
             window(num);
           } while(ch!=59);
           deactivate(num);
         }


         /* Интерактивное перемещение окна */
         void move(num)
         int num;
         {
           char ch;
           int x, y, ex, ey, startx, starty, endx, endy;

           /* активировать, при необходимости */


                                 
Глава II                                                       -- 34 --


           if(!frame[num].active) window(num);

           startx = x = frame[num].startx;
           starty = y = frame[num].starty;
           endx = ex = frame[num].endx;
           endy = ey = frame[num].endy;
           window_xy(num, 0, 0);

           do {
             ch = get_special();
             switch(ch) {
               case 75:   /* влево */
               starty--;
               endy--;
               break;
               case 77:   /* вправо */
               starty++;
               endy++;
               break;
               case 72:   /* вверх */
               startx--;
               endx--;
               break;
               case 80:   /* вниз */
               startx++;
               endx++;
               break;
               case 71:   /* влево вверх */
               startx--; starty--;
               endx--; endy--;
               break;
               case 73:   /* вправо вверх */
               startx--; starty++;
               endx--; endy++;
               break;
               case 79:   /* влево вниз */
               startx++; starty--;
               endx++; endy--;
               break;
               case 81:   /* вправо вниз */
               startx++; starty++;
               endx++; endy++;
               break;
               case 60:    /* F2: отменить и вернуться к исходным
                                                         размерам */
                 startx = x;
                 starty = y;
                 endx = ex;
                 endy =ey;
                 ch = 59;
         }

         /* см. при выходе за диапазон */
         if(startx<0) {


                                 
Глава II                                                       -- 35 --


           startx++;
           endx++;
         }
         if(endx>=25) {
           startx--;
           endx--;
           }
           if(starty<0) {
             starty++;
             endy++;
           }
           if(endy>=79) {
             starty--;
             endy--;
           }
           deactivate(num);
           frame[num].startx = startx;
           frame[num].starty = starty;
           frame[num].endx = endx;
           frame[num].endy = endy;
           window(num);
         } while(ch!=59);
         deactivate(num);
       }


         /* Вывести текст заголовка, начиная с определенной позиции */
         void display_header(num)
         int num;
         {
           register int y, len;

           y = frame[num].starty;

           /* Вычислить начальную позицию относительно центра текста
           заголовка, если отрицательная, то текст не подходит */
           len = strlen(frame[num].header);
           len = (frame[num].endy - y - len) / 2;
           if(len<0) return;  /* не выводить на экран */
           y = y + len;

           write_string(frame[num].startx, y,
                      frame[num].header, NORM_VID);
         }

         void draw_border(num)
         int num;
         {
           register int i;
           char far *v, far *t;

           v = vid_mem;
           t = v;
           for(i=frame[num].startx+1; i<frame[num].endx; i++) {


                                 
Глава II                                                       -- 36 --


           v += (i*160) + frame[num].starty*2;
           *v++ = 179;
           *v = NORM_VID;
           v = t;
           v += (i*160) + frame[num].endy*2;
           *v++ = 179;
           *v = NORM_VID;
           v = t;
         }
         for(i=frame[num].starty+1; i<frame[num].endy; i++) {
            v += (frame[num].startx*160) + i*2;
            *v++ = 196;
            *v = NORM_VID;
            v = t;
            v+ =(frame[num].endx*160) + i*2;
            *v++ = 196;
            *v = NORM_VID;
            v = t;
         }
      write_char(frame[num].startx, frame[num].starty, 218, NORM_VID);
      write_char(frame[num].startx, frame[num].endy, 191, NORM_VID);
      write_char(frame[num].endx, frame[num].starty, 192, NORM_VID);
         write_char(frame[num].endx, frame[num].endy, 217, NORM_VID);

         }


     /***************************************************************/
     /*   Оконные функции ввода/вывода                              */
     /***************************************************************/

     /* Вывести символ в текущую позицию курсора в созданном окне.
        Возвратить 0, если окно не активное и 1 в противном случае. */
         window_puts(num, str)
         int num;
         char *str;
         {

           /* убедиться, что окно активное */
           if(!frame[num].activite) return 0;

           for( ; *str; str++)
             window_putchar(num, *str);
           return 1;
         }

      /* Вывести символ в текущую позицию курсора в созданном окне.
         Возвратить 0, если окно не активное и 1 в противном случае */
         window_putchar(num, ch)
         int num;
         char ch;
         {
           register int x, y;
           char far *v;


                                 
Глава II                                                       -- 37 --



           /* убедиться, что окно активное */
           if(!frame[num].active) return 0;

           x = frame[num].curx + frame[num].startx + 1;
           y = frame[num].cury + frame[num].starty + 1;

           v = vid_mem;
           v += (x*160) + y*2;  /* вычислить адрес */
           if(y>=frame[num].endy) {
             return 1;
           }
           if(x>=frame[num].endx) {
             return 1;
           }
           if(ch=='\n') {   /* символ перехода на следующую строку */
             x++;
             y = frame[num].startx+1;
             v = vid_mem;
             v += (x*160) + y*2;  /* вычислить адрес */
             frame[num].curx++;   /* нарастить x */
             frame[num].cury = 0; /* спросить y */
           }
           else {
             frame[num].cury++;
             *v++ = ch;  /* вывести символ */
             *v++ = NORM_VID; /* нормальные видеоатрибуты */
           }
           window_xy(num, frame[num].curx, frame[num].cury);
           return 1;
         }


         /* Установить курсор в оределенной позиции окна.
            Возвратить 0 при выходе за границу, не ноль в противном
            случае */
         window_xy(num, x, y)
         int num, x, y;
         {
           if(x<0 || x+frame[num].startx>=frame[num].endx-1)
             return 0;
           if(y<0 || y+frame[num].starty>=frame[num].endy-1)
             return 0;
           frame[num].curx = x;
           frame[num].cury = y;
           goto_xy(frame[num].startx+x+1, frame[num].starty+y+1);
           return 1;
         }

         /* Считать строку из окна */
         void window_gets(num, s)
         int num;
         char *s;
         {


                                 
Глава II                                                       -- 38 --


           char ch, *temp;

           temp = s;
           for(,,) {
             ch = window_getche(num);
             switch(ch) {
               case '\r':  /* нажата клавиша ENTER */
                 *s='\0';
                 return;
               case BKSP:  /* возврат */
                 if(s>temp) {
                   s--;
                   frame[num].cury--;
                   if(frame[num].cury<0) frame[num].cury = 0;
                     window_xy(num, frame[num].curx, frame[num].cury);
                      write_char(frame[num].startx+ frame[num].curx+1;
                  frame[num].starty+frame[num].cury+1, ' ', NORM_VID);
                 }
                 break;
               default: *s = ch;
                 s++;
             }
           }
         }



         /* Ввести символ в окно с клавиатуры.
            Возвратить полный 16-ти разрядный скан-код */
         window_getche(num)
         int num;
         {
            union inkey {
              char ch[2];
              int i;
             } c;

             if(!frame[num].active) return 0;  /* окно не активное */

             window_xy(num, frame[num].curx, frame[num].cury);

             c.i = bioskey(0);   /* ввести символ с клавиатуры */

             if(c.ch[0]) {
               switch(c.ch[0]) {
                 case '\r':    /* нажата клавиша ENTER */
                   break;
                 case BKSP:   /*возврат */
                   break;
                 default:
           if(frame[num].cury+frame[num].starty < frame[num].endy-1) {
           write_char(frame[num].startx+ frame[num].curx+1,
           frame[num].starty+frame[num].cury+1, c.ch[0], NORM_VID);
                     frame[num].cury++;


                                 
Глава II                                                       -- 39 --


                   }
               }
            if(frame[num].curx < 0) frame[num].curx = 0;
            if(frame[num].curx+frame[num].startx > frame[num].endx-2)
                 frame[num].curx--;
               window_xy(num, frame[num].curx, frame[num].cury);
             }
             return c.i;
           }

           /* Очистить окно */
           void window_cls(num)
           int num;
           {
             register int i,j;
             char far *v, far *t;

             v = vid_mem;
             t = v;
             for(i=frame[num].starty+1; i<frame[num].endy; i++)
               for(j=frame[num].startx+1; j<frame[num].endy; j++) {
                 v = t;
                 v += (j*160) + i*2;
                 *v++ = ' ';    /* вывести пробел */
                 *v = NORM_VID; /* нормальные видеоатрибуты */
             }
             frame[num].curx = 0;
             frame[num].cury = 0;
           }

           /* Очистить до конца строки */
           void window_cleol(num)
           int (num);
           {
           register int i, x, y;

           x = frame[num].curx;
           y = frame[num].cury;
           window_xy(num, frame[num].curx, frame[num].cury);

           for(i=frame[num].cury; i<frame[num].endy-1; i++)
             window_putchar(num,' ');
           window_xy(num, x, y);
         }


         /* Переместить курсор на одну строку вверх.
            Возвратить ненулевой код в случае успеха, 0 - в пртивном
            случае. */
         window_upline(num)
         int num;
         {
           if(frame[num].curx>0) {
             frame[num].curx--;


                                 
Глава II                                                       -- 40 --


             window_xy(num, frame[num].curx, frame[num].cury);
             return 1;
           }
           return 0;
         }


         /* Переместить курсор на одну строку вниз.
            Возвратить ненулевой код в случае успеха, 0 - в противном
            случае. */

         window_dowline(num)
         int num,
         {
           if(frame[num].curx<frame[num].endx-frame[num].startx-1) {
             frame[num].curx++;
             window_xy(num, frame[num].curx, frame[num].cury);
             return 1;
           }
           return 1;

           /* стереть предыдущий символ
           window_bksp(num)
           int (num);
           {
           if(frame[num].cury>0) {
             frame[num].cury--;
             window_xy(num, frame[num].curx, frame[num].cury);
             window_putchar(num, ' ');
             frame[num].cury--;
             window_xy(num, frame[num].curx, frame[num].cury);
           }
         }


     /***************************************************************/
     /*   Дополнительные функции                                    */
     /***************************************************************/

         /* Вывести на экран строку с дополнительными атрибутами */
         void write_strihg(x, y, attrib)
         int x, y;
         char *p;
         int attrib;
         {
           register int i;
           char far *v;

           v = vid_mem;
           v += (x*160) + y*2; /* вычислить адрес */
           for(i=y; i++) {
             *v++ = *p++;  /* вывести символ */
             *v++ = attrib; /* вывести атрибуты */
           }


                                 
Глава II                                                       -- 41 --


         }

         /* Вывести символы с определенными атрибутами */
         void write_char(x, y, ch, attrib)
         int x, y;
         char ch;
         int attrib;
         {
           register int i;
           char far *v;

           v = vid_mem;
           v += (x*160) + y*2;
           *v++ = ch;   /* вывести символ */
           *v = attrib; /* вывести атрибуты */
         }

         /* Сохранить содержимое части экрана */
         void save_video(num)
         int num;
         {
           register int i, j;
           char *buf_ptr;
           char far *v, far *t;

           buf_ptr = frame[num].p;
           v = vid_mem;
           for(i=frame[num].starty; i<frame[num].endy+1; i++)
             for(j=frame[num].startx; j<frame[num].endx+1; j++) {
               t = (v + (j*160) + i*2);
               *buf_ptr++ = *t++;
               *buf_ptr++ = *t;
               *(t-1) = ' ';  /* очистить окно */
             }
         }


         /* Восстановить содержимое части экрана */
         void restore_video(num)
         int num;
         {
           register int i,j;
           char far *v, far *t;
           char *buf_ptr;

           buf_ptr = frame[num].p;
           v = vid_mem;
           t = v;
           for(i=frame[num].starty; i<frame[num].endy+1; i++)
             for(j=frame[num].startx; i<frame[num].endx+1; j++) {
               v = t;
               v += (j*160) + i*2;
               *v++ = buf_ptr++;   /* вывести символ */
               *v = *buf_ptr++;    /* вывести атрибуты */


                                 
Глава II                                                       -- 42 --


           }
           frame[num].active = 0;
         }


         /* Очистить экран */
         void cls()
         {
           union REGS r;
           r.h.ah=6;   /* код прокрутки экрана */
           r.h.al=0;   /* код очистки экрана */
           r.h.ch=0;   /* начальный ряд */
           r.h.cl=0;   /* начальный столбец */
           r.h.dh=24;  /* конечный ряд */
           r.h.dl=79;  /* конечный столбец */
           r.h.bh=7;   /* пустая строка - черная */
           int86(0x10, &r, &r);
         }

         /* Установить курсор в позицию с координатами x, y */
         void goto_xy(x,y)
         int x,y;
         {
           union REGS r;

           r.h.ah=2; /* функция адресации курсора */
           r.h.dl=y; /* координаты столбца */
           r.h.dh=x; /* координаты ряда */
           r.h.bh=0; /* страница видеопамяти */
           int86(0x10, &r, &r);
         }

     /* Возвратить позиционный код стрелки и функциональных клавиш */
         get_special()
         {
           union inkey {
             char ch[2];
             int i;
           } c;

           /* while(!bioskey(1)) ; /* ждать нажатия клавиши */
           c.i = bioskey(0);       /* считать код нажатой клавиши */

           return c.ch[1];
         }

         /* Возвратить код текущего видеорежима */
         video_mode()
         {
           union REGS r;

           r.h.ah = 15;  /* получить код видеорежима */
           return int86(0x10, &r, &r) & 255;
         }


                                 
Глава II                                                       -- 43 --



         is_in(s, c)
         char *s, c;
         {
           register int i;

           for(i=0; *s; i++) if(*s++==c) return i+1;
           return 0;
         }


         #include "ctype.h"
     /***************************************************************/
     /*   Функции управления всплывающими окнами                    */
     /***************************************************************/

         #define MAX 100

         int *p;  /* указатель стека */
         int *tos; /* указатель вершины стека */
         int *bos; /* указатель дна стека */


     /* Стековый, с постфиксной записью калькулятор с 4-мя функциями*/
         void calc()
         {
           char in[80], out[80];
           int answer, stack[MAX];
           int a,b;

           p = stack;
           tos = p;
           bos = P+MAX-1;

           window(2);
           do {
             window_xy(2, 0, 0);
             window_cleol(2);
             window_puts(2, ": "); /* промтер калькулятора */
             window_gets(2, in);
             window_puts(2, "\n");
             window_cleol(2);
             switch(*in) {
               case '+':
                 a = pop();
                 b = pop();
                 answer = a+b;
                 push(a+b);
                 break;
               case '-':
                 a = pop();
                 b = pop();
                 answer = b-a;
                 push(b-a);


                                 
Глава II                                                       -- 44 --


                 break;
               case '*':
                 a = pop();
                 b = pop();
                 answer = b*a;
                 push(b*a);
                 break;
                 case '/':
                   a = pop();
                   b=pop();
                   if(a==0) {
                       window_puts("divide by 0\n");
                       break;
                   }
                   ansver = b/a;
                   push(b/a);
                   break;
                 default:
                   push(atoi(in));
                   continue;
               }
               sprintf(out, "%d", answer);
               window_puts(2, out);
             } wile(*in);
             deactivate(2);
           }


           /* Поместить число в стек.
              Возвратить 1 в случае успеха и 0 в противном случае */
           push(i)
           int i;
           {
             if(p>bos) return 0;

             *p=i;
             p++;
             return 1;
           }


           /* Извлечь верхний элемент из стека.
              Возвратить 0 если стек пуст */
           pop()
           {
             p--;
             if(p<tos) {
               p++;
               return 0;
             }
             return *p;
           }




                                 
Глава II                                                       -- 45 --



           /* Десятично-шестнадцатиричный преобразователь */
           void dectohex()
           {
             char in[80], out[80];
             int n;

             window(1);
             do {
     window_xy(1, 0, 0);  /* перейти к первой строке */
     window_cleol(1);     /* очистить строку */
     window_puts(1, "dec: ");  /* промтер */
     window_gets(1, in);       /* считать число */
     window_putchar(1, '\n');  /* перейти к следующей строке */
     window_cleol(1);          /* очистить ее */
     sscanf(in,"%d", &n);      /* преобразовать во внутренний формат*/
     sprintf(out, "%s%X", "hex: ",n); /* преобразовать в
                                    шестнадцатиричное представление */
            window_puts(1, out); /* вывести шестнадцатиричное число */
             } wile(*in);
             deactivate(1);
           }


           /* Всплывающая записная книжка */
           #define MAX_NOTE 10
           #define BKSP 8
           char notes[MAX_NOTE][80];

           void notepad()
           {
             static first=1;
             register int i; j;
             union inkey {
               char ch[2];
               int i;
             } c;
             char ch;

          /* инициализировать массив записей, если это необходимо */
             if(frist) {
               for(i=0; i<MAX_NOTE; i++)
                 *notes[i] = '\0';
               first = !first;
           }

           window(3);
           /* вывести на экран существующие записи */
           for(i=0; i<MAX_NOTE; i++) {
             if(*notes[i]) window_puts(3, notes[i]);
             window_putchar(3, '\n');
           }

           i=0;


                                 
Глава II                                                       -- 46 --


           window_xy(3, 0, 0);

           for(;;) {
             c.i = bioskey(o);  /* считать код клавиши */
             if(tolower(c.ch[1])==59) {  /* F1 - для входа */
               deactivate(3);
               break;
             }

             /* если обычная клавиша */
             if(isprint(c.ch[0]) || c.ch[0]==BKSP) {
               window_cleol(3);
               notes[i][0] = c.ch[0];
               j = 1;
               window_putchar(3, notes[i][0]);
               do {
                 ch = window_getche(3);
                 if(ch==BKSP) {
                   if(j>0) {
                     j--;
                     window_bksp(3);
                       }
                     }
                     else {
                       notes[1][j] = ch;
                       j++;
                     }
                   } while(notes[i][j-1]!='\r');
                   notes[i][j-1] = '\0';
                   i++;
                   window_putchar(3, '\n');
                 }
                 else {    /* если специальная клавиша */
                   switch(c.ch[1]) {
                     case 72:     /* стрелка вверх */
                       if(i>0) {
                         i--;
                         window_upline(3);
                       }break
                     case 80:     /* стрелка вниз */
                       if(i<MAX_NOTE-1) {
                         i++;
                         window_dowline(3);
                       }
                       break;
                   }
                 }
               }
             }


          Пять первых  функциональных   клавиш   выполняют   следующие
     действия:



                                 
Глава II                                                       -- 47 --


             F1 - демонстрирует работу функции window_xy()
             F2 - демонстрирует изменение размера и положения окна
             F3 - вызывает калькулятор
             F4 - вызывает    программу   десятично-шестнадцатиричного
                  преобразования
             F5 - вызывает "записную книжку".

          Рисунки 2-1, 2-2 и  2-3  иллюстрируют  появление  на  экране
     соответствующих меню.  Рис. 2-4 иллюстрирует изменение размеров и
     положения     окна     программы     десятично-шестнадцатиричного
     преобразования.






                                 
Глава II                                                       -- 48 --


               Модификации программ управления окнами
     -----------------------------------------------------------------

          При использовании представленных в данном  разделе  программ
     управления  окнами  вы  всегда  должны  точно определять окно,  с
     которым  собираетесь  работать.  Этот  способ  при  использовании
     программ обеспечивает большую гибкость.  Другой способ управления
     окнами основан на применении стека.


     -------------------- Editor [Esc to exit] --------------------¬
     ¦                                                             ¦
     ¦  To whom it may concern:                                    ¦
     ¦                                                             ¦
     ¦  This is to inform you D. W. Porkbellies will no lohger     ¦
     ¦  be providing its customers with the following products:    ¦
     ¦                                                             ¦
     ¦                                                             ¦
     ¦                                                             ¦
     ¦                  ------------- Calculator --------------¬   ¦
     ¦                  ¦                                      ¦   ¦
     ¦                  ¦ :                                    ¦   ¦
     ¦                  ¦                                      ¦   ¦
     ¦                  ¦                                      ¦   ¦
     ¦                  ¦                                      ¦   ¦
     ¦                  ¦                                      ¦   ¦
     ¦                  L---------------------------------------   ¦
     L--------------------------------------------------------------

                          Рис. 2-1 Окно калькулятора.

     --------------------- Editor [Esc to exit] ------------------¬
     ¦                                                            ¦
     ¦  To whom it may concern:                                   ¦
     ¦                                                            ¦
     ¦  This is to inform you D. W. Porkbellies will no lohger    ¦
     ¦  be providing its customers with the following products:   ¦
     ¦                                                            ¦
     ¦                                                            ¦
     ¦                             --------- Decimal to Hex ---¬  ¦
     ¦                             ¦                           ¦  ¦
     ¦                             ¦ dec: 12                   ¦  ¦
     ¦          . meat and oats fak¦ hex: C                    ¦  ¦
     ¦                             ¦                           ¦  ¦
     ¦          . lime cola drink pL----------------------------  ¦
     ¦                                                            ¦
     ¦          . syrup coated sizzle links                       ¦
     ¦                                                            ¦
     ¦                                                            ¦
     L-------------------------------------------------------------

         Рис. 2-2 Окно десятично-шестнадцатиричного преобразователя.




                                 
Глава II                                                       -- 49 --


     -------------------- Editor [Esc to exit] ------------------¬
     ¦                                                           ¦
     ¦ To whom it may concern:                                   ¦
     ¦                                                           ¦
     ¦ This is to inform you D. W. Porkbellies will no lohger    ¦
     ¦ be providi---------- Notepad [F1 to exit] ---------¬      ¦
     ¦           ¦                                        ¦      ¦
     ¦           ¦ call Sherry                            ¦      ¦
     ¦           ¦ go to the store                        ¦      ¦
     ¦           ¦                                        ¦      ¦
     ¦           ¦                                        ¦      ¦
     ¦           ¦                                        ¦      ¦
     ¦           ¦                                        ¦      ¦
     ¦           L-----------------------------------------      ¦
     ¦                                                           ¦
     L------------------------------------------------------------

              Рис. 2-3    Окно записной книжки.




     -------------------- Editor [Esc to exit] -----------------¬
     ¦                                                          ¦
     ¦ To whom it may concern:                                  ¦
     ¦                                                          ¦
     ¦ This is to inform you D. W. Porkbellies will no lohger   ¦
     ¦ be providing its customers with the following products:  ¦
     ¦                                                          ¦
     ¦              . meat and oats fake burgers                ¦
     ¦                                                          ¦
     ¦              . lime cola drink pops                      ¦
     ¦                                                          ¦
     ¦                                                          ¦
     ¦     ------ Decimal to Hex --------¬ le links             ¦
     ¦     ¦                             ¦                      ¦
     ¦     ¦                             ¦                      ¦
     ¦     ¦                             ¦                      ¦
     ¦     L------------------------------                      ¦
     L-----------------------------------------------------------



               Рис. 2-4  Изменение размеров и положения окна
               десятично-шестнадцатиричного преобразователя.

          В этом  случае  оконные  программы  ввода-вывода не получают
     номер  окна  в  качестве  аргумента.  Вместо  этого  номера  окон
     помещаются в стек в том порядке, в котором они были активированы.
     Оконные программы всегда работают с  тем  окном,  номер  которого
     находится  в  вершине  стека.  При  деактивации  окна  его  номер
     извлекается из стека.  Преимущество этого способа  заключается  в
     том,  что  вы  не должны в этом случае думать о номерах окон.  Вы
     можете модифицировать  программы  управления  окнами,  чтобы  они


                                 
Глава II                                                       -- 50 --


     работали именно этим способом.

          Другая модификация    может   заключаться   в   том,   чтобы
     обеспечивать прокрутку окна,  когда курсор достигает  его  нижней
     границы.  В  существующем  варианте,  если  курсор  достиг нижней
     границы, то после нажатия клавиши Ввод ничего не  происходит.  Вы
     однако  можете  изменить  программы таким образом,  чтобы верхняя
     строка пропадала, а внизу появлялась новая пустая строка.

          Наконец, те читатели,  которые имеют цветные дисплеи,  могут
     использовать  различные  цвета  для  обозначения границ различных
     окон.  При правильном применении  это  добавит  привлекательности
     вашим программам.

[ Назад ] [ Далее ]

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог