Ввод и вывод на основе conio.h


Детальное описание примеров

Если мы хотим использовать больше возможностей, чем создавать файлы диагностики или выдавать строчку ╚здравствуй мир╩, то нам надо определить удобный ввод с клавиатуры от горячих клавиш и окна отображения. В сам язык не входят средства ввода-вывода. Стандартная библиотека предоставляет работу с потоками, но мы пока не будем писать то, что нам надо через потоки, так как это хотя и здорово, но сложно. Пока привяжемся конкретно к двум устройствам - окно текстового режима ДОС или WIN32 и клавиатура. Драйвером с этими устройствами для нас будет та часть библиотеки компилятора, которая отражена в conio.h.


Клавиатура Tkeyb (keyb.h)

Ввод и вывод на основе conio.h

Описание

Клавиатура Tkeyb (keyb.h)

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

Непосредственно с клавишей или сочетанием связана функция фиксированного типа - функция отклика, которая может быть сразу дружественной какому-то классу, либо, что лучше, вызывать сообщение, может быть виртуальное, уже принадлежащее какому-то классу. Сама функция отклика определена как не принадлежащая ни к какому объекту. Функция отклика устанавливается сообщением install() или replace(). Только один набор функций отклика доступен в каждый момент времени.

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

Организация объекта

Клавиатура Tkeyb (keyb.h)

Вот классы и типы, используемые для клавиатуры:
Кнопка
    class    Tkeyb_key;
Элемент таблицы реакций на кнопку
    class    Tkeyb_action;
Таблица реакций
    class    Tkeyb_actions;
Клавиатура
    class    Tkeyb;
Тип функции отклика t_keyb_action_f
    void     ( *t_keyb_action_f )( Tkeyb_key key, void_p obj_p );

Объект Tkeyb_key описывает кнопку или сочетание, которое можно получить от getch(). Эта функция возвращает или символ, или два, если первый символ равен нулю. Это позволяет иметь 2*256-1 отдельных сочетаний клавиш, но не все используются. Посмотреть сочетания клавиш, которые возвращаются getch(), можно для ДОС утилитой keyview(download keyview). Этот объект содержит тип кнопки ( KEY_EXT для двухбайтового кода ) и сам код.

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

Все элементы таблицы реакций объединены в одну таблицу реакций на кнопки Tkeyb_actions. Этот объект имеет поле таблица кнопок, как массив [2][256]. Первый индекс соответствует типу кнопки Tkeyb_key, второй коду кнопки. Таблицу можно скопировать в другую или очистить сообщением clear().

Функцию отклика удобно обявлять и определять, используя макрос KEYB_ACTION(имя_функции). Параметры, нажатая кнопка и указатель на объект, могут использоваться для дополнительной селекции, если одна функция отклика отвечает за несколько кнопок.

Создание и удаление объекта

Клавиатура Tkeyb (keyb.h)

Создать объект можно без или по готовой таблице реакций. Мы будем использовать только конструктор по умолчанию и один глобальный объект клавиатуры. При удалении деструктор освободит память, выделенную конструктором по умолчанию под таблицу реакций.

Основные операции

Клавиатура Tkeyb (keyb.h)

Мы будем использовать такие сообщения Tkeyb
обработать нажатие кнопки с ожиданием/без
    t_err        action( int wait=KEYB_ACTION_WAIT );
установить действие на нажатие кнопки
    t_err        install( Tkeyb_key key, 
                          Tkeyb_action& new_action=KEYB_DEF_ACTION );
поменять действие на нажатие кнопки
    Tkeyb_action replace( Tkeyb_key key,  
                          Tkeyb_action& new_action=KEYB_DEF_ACTION );

Параметр wait в сообщении action() задает опрос клавиатуры либо с ожиданием нажатия, либо без для выполнения еще и других задач в цикле, так как наша среда выполнения не многозадачна и мы не используем прерывания. Сообщение replace() устанавливает действие и возвращает предыдущую функцию отклика. Эти сообщения так же имеют форму для работы без объекта получателя сообщения от нажатия кнопки. Подробности в листинге.

Другие операции

Клавиатура Tkeyb (keyb.h)

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

    Tkeyb_actions_p    get_keyb_actions();

Простой десктоп Tdesktop (easywin.h)

Ввод и вывод на основе conio.h

Описание

Простой десктоп Tdesktop (easywin.h)

Устоявшимся способом отображения является отображения в логические области экрана - окна. Нам нужны только текстовые окна. Чтобы развязать окна от типа экрана и, возможно, использовать управление сразу всеми окнами на экране, будем использовать что-то вроде драйвера экрана - десктоп. Сам десктоп будет основан на функциях вывода из conio.h.

Организация объекта

Простой десктоп Tdesktop (easywin.h)

Свойствами, доступными окнам десктопа и другим элементам программы являются эти
разрешение экрана
    u_int    lx, ly;
позиция курсора
    u_int    cx, cy;

Хотя эти свойства доступны и для записи, использовать их стоит только для чтения. Позиция курсора считается от 0 и за 0,0 принимается левый верхний угол экрана. Вместе с десктопом ассоциируется объект клавиатуры Tkeyb.

Создание и удаление объекта

Простой десктоп Tdesktop (easywin.h)

Создать объект можно либо конструктором по умолчанию, либо указав уже существующий объект клавиатуры. При удалении деструктор удалит объект клавиатуры, если десктоп был создан конструктором по умолчанию.

Основные операции

Простой десктоп Tdesktop (easywin.h)

Для десктопа будем использовать следующие операции
установить позицию курсора
    void    gotoxy(u_int x, u_int y);
напечатать в позиции курсора
    void    print(char_p str);
напечатать в заданной позиции
    void    printxy(u_int x, u_int y, char_p str);

Другие операции

Простой десктоп Tdesktop (easywin.h)

Вспомогательными операциями являются
Установка разрешения экрана
    t_err    resolution(int mode);
Установка типа курсора
    t_err    set_cursor_type(int type=_NOCURSOR);

Допустимые параметры type и mode определены для BC45. Под другим компилятором, возможно, придется корректировать код методов этих сообщений.


Простое окно Twin (easywin.h)

Ввод и вывод на основе conio.h

Описание

Простое окно Twin (easywin.h)

Под окном Twin мы будем понимать объект, который

Содержимое хранится в списке lines. Свойствами отображения являются
размер окна
    u_int    lx, ly;
цвет
    u_int    color, bkcolor;

Цвета задаются и читаются сообщением =(присвоить) и имеют действие при следующей регенерации окна. Размер окна устанавливается сообщением set_size(). Заметим, что в свойства отображения не входят координаты окна в десктопе или где нибудь еще, как в десктоп не входят положение ручек ╚яркость╩ и ╚контрастность╩ на мониторе. Для организации различного вида окон, будем использовать список children дочерних окон. Наши дочерние окна позволят создать такие элементы окна, как

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

Для отображения данных окна и всех его дочерних окон нам, подобно случаю с клавиатурой, придется самим вызывать сообщение refresh(). Если нужно установить окно и все дочерние только согласно цветам bk_color, то надо вызвать сообщение clear(). Для управления видимостью окна и его дочерних при отображении используется сообщение hide().

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

Настырный читатель рассмотрев все задачи, которые выполняет окно, может быть захочет выделить отдельно какие-нибудь такие абстракции, как

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

Организация объекта

Простое окно Twin (easywin.h)

Наличие в объекте окна свойств по управлению отображением и отсутствие автоматического вызова регенерации говорит о том, что часть горячих клавиш потребует перерисовки окна изнутри окна. Для этого надо знать позицию окна в десктопе. Эта позиция сохраняется от последнего вызова refresh() или clear() в last_x и last_y, поэтому после первой установки позиции окна, его легко перерисовать изнутри самого окна.

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

С окном связан объект десктоп. Если при создании первого окна десктоп еще не создан, то он будет создан автоматически и удален, при удалении последнего экземпляра окна.

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

Элемент списка children дочерних окон описывается классом Twin_list_item. Этот объект содержит координаты дочернего окна со знаком x,y, относительно позиции (0,0) родительского и ссылку win на само дочернее окно. Признак win_need_free позволит удалить дочернее окно в деструкторе родителя.

Любой потомок Twin, который хочет, чтобы его главное окно отображалось, должен определить метод для set_size(), так так Twin::set_size() только вызывает этот метод для окон из списка дочерних children. Аналогично, метод активизации окна Twin::sw_active() только вызывает этот метод для окон из того-же списка.

Создание и удаление объекта

Простое окно Twin (easywin.h)

Создание конструктором по умолчанию, удаление деструктором. Подробности в листинге.


Окно с заголовком Tcwin (winitems.h)

Ввод и вывод на основе conio.h

Описание

Окно с заголовком Tcwin (winitems.h)

Это окно добавляет к Twin строку заголовка. Чтобы эта строка не мешалась с пользовательским реальным окном, заголовок оформляется как отдельное окно-наследник Twin, класс Tcwin_caption.

Организация объекта

Окно с заголовком Tcwin (winitems.h)

Заголовок и пользовательское окно добавляются в список дочерних окон Tcwin. Все декорации пользовательского окна размещаются в списке дочерних окон этого пользовательского окна. Это окно, как и заголовок, должно быть потомком простого окна Twin. Само окно Tcwin не имеет отображения, отображается только заголовок и рабочее пользовательское окно.

Создание и удаление объекта

Окно с заголовком Tcwin (winitems.h)

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

Основные операции

Окно с заголовком Tcwin (winitems.h)

Это работа с заголовком и с пользовательским окном сообщениями [set_|get_]caption() и [set_|get_]paper(). Вот методы, которые реализованы для них в Tcwin
установить заголовок окна
virtual    void set_caption(char_p caption,char skip_set_size=0);
получить доступ к окну заголовка
virtual    Twin_p get_caption();
установить новое рабочее окно
virtual    void set_paper(Twin_p paper_p,char skip_set_size=0);
получить доступ к рабочему окну
virtual    Twin_p get_paper();

Параметр skip_set_size==1 позволяет не вызывать настройку размеров дочернего окна согласно размерам Tcwin, если на этом этапе этот размер еще не определен. Ничего страшного не произойдет, если вызов все-же будет сделан, но это просто трата времени.

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


Диалог Tdialog (winitems.h)

Ввод и вывод на основе conio.h

Описание

Диалог Tdialog (winitems.h)

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

Организация объекта

Диалог Tdialog (winitems.h)

Окно будет отображено по центру экрана. Чтобы убрать отображение окна диалога после выхода из диалога, надо обновить содержимое окон, которые оно перекрывало. Это окно наследуется из Тcwin. Реальный текст, который будет отображаться, определяется в конструкторе потомка или сообщением set_paper() для этого класса Tdialog, если диалоговое окно просто выводит сообщение. По умолчанию, рабочее окно не существует, заголовок пуст. Особенности изменения взаимных размеров окна в зависимости от содержимого заголовка в листинге set_size().

Создание и удаление объекта

Диалог Tdialog (winitems.h)

Объект не имеет своих конструктора и деструктора, и создается так-же как окно с заголовком Тcwin.

Основные операции

Диалог Tdialog (winitems.h)

Это такие операции
исполнить c очисткой/без кнопок других окон
virtual    u_int execute(char clear_keys=0);
узнать нажато Enter или ESC
char       get_exit_status();

Параметр clear_keys==DLG_CLEAR_KEYS позволит не передавать сообщения от клавиатуры для других окон, как модальный диалог. Тип отмены или подтверждения диалога DLG_CANCEL или DLG_OK. Метод для сообщения execute() в этом классе возвращает нажат выбор или отмена, в вашем же классе, который будет его наследником, он может возвращать что угодно, например, номер выбранной строки списка.


Ввод и вывод на основе conio.h