|
Техника
программирования на Turbo C
Глава 8
Автор: Ал. Стивенс
Иcпользование данных в окнах
Интерактивные программы интерпретируют
значения данных внутри компьютера в соответствии с типами данных, определенными
пользователем. Эти значения данных могут интерпретироваться по-разному,
поэтому программы могут использовать разнообразные технические приемы
для интерпретации значений вводимых данных. Например, текстовый процессор
интерпретирует данные, поступающие на его вход, как текст произвольной
формы. Электронный бланк интерпретирует данные в соответствии с совокупностью
форматов строки и столбца, в которой в данный момент находится курсор.
Однако одни и те же данные каждая программа "видит и чувствует"
по-разному. Поэтому даже правильное копирование входного потока одной
программы на вход другой может привести к массе спорных результатов,
которые в этом случае выдает вторая программа.
Многие интерактивные программы воспринимают
значения данных непосредственно в той форме, в которой пользователь
осуществляет их физический ввод (например, данные в печатной форме (бланк)).
Каждая, сходная с печатной, форма представления данных определялась
Государственной налоговой службой. Формо- ориентированные интерактивные
программы интерпретируют программы как лист бумаги, содержащий поля
со значениями данных и текст, описывающий значение каждого поля. После
того, как пользователь введет значение данного в соответствующие позиции
поля, программное обеспечение проверяет допустимость введенного значения
и дальнейшее управление курсором. В этой главе формо-ориентированный
ввод данных, указание доступа к ним и контроль операций над ними реализован
путем использования окна как шаблона для ввода данных. Шаблон ввода
данных включает в себя окно вместе с описанием вводимых в каждое поле
данных и именами полей ввода данных.
Шаблон ввода данных
Перед вводом данных в окно вы должны
прежде всего открыть окно, задать атрибуты шаблона ввода данных, соответствующие
описанию данных в прикладной программе, обрабатывающей их, а также описать
расположение полей ввода данных и присвоить им имена. После того, как
шаблон готов, вы можете вызвать функцию ввода данных, которая осуществит
управление чтением всех элементов данных с шаблона. По завершению работы
функции, когда все значения элементов данных размещаются в указанном
вами буфере, производится проверка на их допустимость, преобразование
по указанным форматам и считывание в некоторой последовательности для
обработки вашей прикладной программой.
Поле ввода данных
Поле ввода данных, по сути дела,
представляет собой элемент данных. Оно может быть датой, результатом
вычисления суммы чего-либо, именем или вычисленной средней заработной
платой. Поле ввода данных имеет следующие характеристики: оно имеет
определенную длину и формат и должно занимать одну строку окна. Поле
ввода данных - традиционный элемент систем управления базами данных
(СУБД).
После того, как вы описали поля шаблона,
вы должны специфицировать позицию размещения поля в шаблоне, атрибуты,
выходной буфер, функцию проверки допустимости данных, help-информацию
и маску вводимых данных для каждого поля. Эти компоненты описания полей
рассматриваются ниже.
Позиция
Позиция поля задается выражением,
состоящим из номера строки и столбца символьных координат, соответствующих
местоположению текста в шаблоне окна. Предпочтительнее задавать координаты
позиции поля относительно координат окна, а не экрана; если вы вдруг
решите изменить координаты расположения самого окна, то в случае описания
позиции поля в координатах окна описание позиции поля вам изменять не
придется.
Атрибуты
Атрибут поля позволяет описать
вводимые в него данные как имеющие один из существующих типов данных.
Тип данных задается отдельной буквой (которые рассмотрены в данной главе).
Однако уже сейчас вам необходимо знать, что вы можете специфицировать
поле как поле даты, спецификации, денежной единицы или как числовое.
Задание атрибута поля позволяет контролировать, каким способом будут
интерпретированы значения данных в поле.
Буфер
Для каждого поля назначается выходной
буфер данных, который является адресом символьного массива, в котором
будет происходить накопление данных. Ваша программа резервирует память
под этот буфер и передает его адрес данным внутри прикладной программы.
Проверка допустимости значений
Ваша программа генерирует адрес
функции проверки допустимости вводимых значений так, как будто сама
эта функция используется. Программное обеспечение ввода данных будет
вызывать эту функцию всегда после того, как вы ввели данное в поле.
Следует заметить, что при вводе данных программное обеспечение само
осуществляет автоматическую проверку некоторых основных ограничений
в соответствии с атрибутами поля, однако, используя дополнительные подпрограммы
проверки значений, вы можете существенно расширить и усложнить проверку
данных по различным критериям.
Help-информация
Вы можете специфицировать вспомогательное
help-окно (которое описано в Главе 7) для каждого поля, а также help-функцию
для каждого поля, которая будет вам выдавать справочную информацию о
предназначении каждого поля в случае, если постоянный текст, описывающий
поле и отображаемый в окне, не удовлетворяет вашему любопытству. Help-окно
получает информацию из специального help-файла, в связи с чем включение
help-функций в программу обязательно. При желании вы можете не специфицировать
свои help-функции, а использовать стандартные help-функции пакета, которые
могут быть вызваны пользователем путем нажатия на соответствующую help-клавишу.
Следует помнить, что использование соответствующей help-спецификации
эффективно лишь в процессе ввода пользователем данных в поле, для которого
эта help-информация предназначена.
Маска вводимых данных
Когда вы определяете поле для ввода
данных, вы можете задать маску для всех вводимых в это поле значений.
Эта маска специфицируется в массиве символов, включающем в себя символы
нижнего подчеркивания и пунктуации. Символ нижнего подчеркивания соответствует
позиции маски, в которой возможен ввод данных, а пунктуация служит для
обозначения других (различных) отображаемых символов кода ASCII. Длина
элементов данных описывается количеством символов нижнего подчеркивания,
а длина буфера, выделяемого под вводимые данные, описываемые маской,
должна быть не меньше, чем длина элемента данных плюс 1. Символы пунктуации
в буфер не перекачиваются. Например, маска вводимых данных, касающихся
номера телефона (включая код местности и расширение) может иметь следующий
вид:
char phone_mask [] = "(____)____-____ ext:_____";
Приглашения к вводу в поле
(Prompts)
Каждое поле может иметь свое специфичное
приглашение к вводу в него данных, которое обычно содержит информацию,
поясняющую пользователю, для чего предназначено данное поле (семантика
поля
и какая именно информация должна быть в него введена. Такое приглашение
представляет собой символьную строку, которую пользователь изменить
не может. Вы должны сами специфицировать содержание этой строки и позицию
ее размещения в окне. Приглашение к вводу данных должно иметь длину,
не превышающую длину строки окна.
Ввод данных
В процессе своей работы функция
ввода данных обрабатывает поля шаблона в той последовательности, в которой
вы осуществляли их описание (не принимая во внимание позиции их размещения
в шаблоне), осуществляя при этом простейшую проверку допустимости находящихся
в полях шаблона данных (правильность даты, поле должно содержать только
цифры и т.д.). Вы можете также определить обычную (в смысле Си) функцию,
которая будет осуществлять дальнейшую, возможно, сложную и разнообразную,
проверку вводимых данных или определить функцию, которая будет выдавать
в случае ввода недопустимых данных специальную help-информацию пользователю.
В процессе ввода данных в шаблон используется выделение в соответствии
с указанными атрибутами (ACCENT) цвета окна более ярким тоном полей,
в которые предполагается ввод данных. При этом курсор на экране компьютера
переходит в соответствующую позицию поля. В случае, если пользователь
инициирует другой порядок ввода данных, переходя от поля к полю, то
выделение следующего, выбранного пользователем поля ввода, и перемещение
курсора также осуществляется программно.
Функции сбора данных
Эти функции, являющиеся библиотечными
функциями, ваша программа так или иначе вызывает при использовании окна
с шаблоном ввода данных. Запомните, что вы должны вначале установить
окно, а лишь затем использовать эти функции.
void init_template(WINDOW *wnd)
Эта функция инициализирует окно
для использования в нем шаблона ввода данных. Она устанавливает связной
список структур FIELD и производит очистку всех существующих дескрипторов.
Структура FIELD, описывающая характеристики полей ввода данных, определена
в twindow.h (см. Главу 6). Функция inint_template предназначена для
выполнения двух задач. Во-первых, она инициализирует окно; во-вторых,
если окно инициализировано, она осуществляет поиск и уничтожение всех
установленных на данный момент полей окна; следовательно, использование
этой функции возможно в ситуации, когда шаблон окна не является достаточно
большим, так как использование функции приводит к освобождению структур
FIELD.
FIELD *establish_field
(WINDOW *wnd,int x,int y,char *msk,char *bf,int t)
Эта функция устанавливает поля ввода данных
в окне, которое было инициализировано функцией init_template как окно
с шаблоном ввода данных. Символы x и y суть координаты, специфицирующие
размещение маски поля ввода, причем предпочтительнее указывать координаты
относительно окна, а не экрана. Указатель bf суть указатель на определяемый
при обращении буфер сбора данных. Параметр t (значение типа integer)
указывает тип поля ввода данных. Различают следующие типы полей:
А = алфавитно-цифровое
N = цифровое, незначащие разряды заполнены пробелами
Z = цифровое, незначащие разряды заполнены нулями
С = поле денежной единицы
D = поле даты
Указатель msk является указателем на
символ маски, который определяет длину поля и управляет отображением
символа пунктуации на экране (причем последние не контролируются в буфер
сбора данных поля). Маска включает в себя обычно несколько символов.
Символ подчеркивания указывает позицию символа, который должен быть
введен в данном месте. Массив bf должен содержать столько символов,
сколько символов подчеркивания содержит маска, плюс один символ (для
символа конца строки \0).
Поле денежной единицы может иметь произвольное
число цифр слева от десятичной точки и две цифры справа от нее.
Дата должна соответствовать формату "ММДДГГ".
Если пользователь неправильно ввел дату, программа выдает сообщение
об ошибке и требует от пользователя повторного ввода даты.
Функция establish_field возвращает указатель
на структуру FIELD, определенную в twindow.h. Вы можете использовать
этот указатель при обращении к field_window, field_help, field_protect
и field_validate, которые описаны ниже.
void wprompt(WINDOW *wnd, int x, int y, char
*s)
Эта функция осуществляет выдачу приглашения
на ввод данных в окно. Приглашение на ввод данных является строкой s,
которая выдается, начиная с координат окна, задаваемых значениями х
и у.
void field_tally(WINDOW *wnd)
При обращении к этой функции на экран
дисплея выводятся значения всех данных, хранящихся в буфере, для всех
полей шаблона. Вы можете использовать эту функцию, когда, например,
вы заполнили буфер значениями из записи базы данных.
void field_window(FIELD *fld, char *helpname,
int x, int y)
Эта функция обеспечивает выдачу
контекстно-зависимого help-окна для каждого поля шаблона. Параметр helpname
является строкой, содержащей мнемонику help-окна в текущем help-файле,
и специфицируется предварительно путем обращения к функции load_help
(см. Главу 7). Точно так же, как функция data_entry осуществляет переход
от поля к полю, обращение к функции set_help для каждого из полей позволяет
привязать конкретное help-окно для каждого из них. Параметры х и у позволяют
специфицировать позицию экрана, с которой будет осуществляться выдача
help-окна.
Заметьте, что эта функция, а также еще
три, рассмотренные ниже, не требуют наличия в списке параметров указателя
WINDOW. Для этих функций достаточно указателей FIELD, так как эти указатели
представляют собой указатели на цепочку связного списка, который и осуществляет
привязку функции к конкретному окну. Эти функции модифицируют определенные
ранее поля, не обращая внимания на то, что они принадлежат конкретному
шаблону окна.
void clear_template(WINDOW *wnd)
Эта функция освобождает буферы
сбора данных всех полей шаблона, обращая их в пустую строку, заканчивающуюся
нулевым символом конца строки, а также отображает на экране все поля
шаблона.
void field_validate(FIELD *fld, int (*validfn)())
Эта функция относится к макросам. Она
использует адрес функции проверки допустимости вводимых значений (если
таковая будет написана пользователем). Стандартные функции, осуществляющие
проверку допустимости вводимых в поля данных, обычно не осуществляют
требуемой проверки на ошибочную ситуацию. По этой причине пользователь
обычно сам пишет функцию, производящую контроль данных. Функция data_entry
будет осуществлять обращение к определенным вами функциям контроля данных
после того, как ею самой будет осуществлен их первичный контроль. Если
это так, то она будет передавать в эти ваши функции адрес буфера сбора
данных, и разработанные вами функции контроля данных смогут осуществить
проверку введенных пользователем значений, находящихся уже в буфере.
Ваша функция контроля допустимости данных может содержать вызов функции
error_message (см. Главу 6) для обработки ситуации,
когда будет обнаружено недопустимое значение. При этом определенная
вами функция контроля данных должна возвращать лишь два значения ОК
или ERROR, которые, в свою очередь, определены в twindow.h. Если функция
возвращает значение ОК, то функция data_entry будет обрабатывать следующее
поле шаблона. Если же функция контроля возвращает значение ERROR, то
функция data_entry "остановится" на поле, в данных которого
обнаружена ошибка.
void field_protect(FIELD *fld, int prot)
Эта функция также относится к макросам.
Она устанавливает или отменяет (в зависимости от значения параметра
prot) защиту характеристик поля. Защищенное поле не обрабатывается (игнорируется)
функцией data_entry. Используя функцию field_protect, вы можете управлять
доступом пользователей к полям шаблона, разрешая или запрещая запись
в них данных.
Эта функция может использоваться совместно
с функциями field _validate, clear_template и field_fally для контроля
над изменениями, производимыми в записях базы данных, в случае, если
элементы записи базы данных отображаются в шаблоне.
Для понимания того, как значительно расширяет
возможности программы использование функции field_protect, рассмотрим
шаблон, изображенный на рисунке 8.1. Примите во внимание, что этот шаблон
содержит поля, образующие запись по одному служащему в файле базы данных.
Шаблон ввода данных используется для ввода, отображения, поиска и изменения
записей в файле. Перед началом ввода данных ваша программа может обратиться
к функции field_protect для защиты всех полей, кроме поля "номер
служащего". Затем вы можете обратиться к функции field_validate,
передав ей указатель на обычные, разработанные вами функции контроля
данных на допустимость. После того, как пользователь введет данные в
поле "номер служащего", функция data_entry (будет описана
позже) осуществит обращение к разработанной вами функции контроля данных,
которая осуществит поиск и сравнение по ключу (номер служащего) нужной
записи в базе данных. Функция контроля данных произведет загрузку соответствующих
буферов накопления данных элементами данных из найденной записи базы
данных, а затем обратится к функции field_fally для вывода значений
элементов данных на экран. После этого будет вызвана функция field_protect
для установки защиты для поля "номер служащего" и снятия защиты
с остальных полей. В итоге функция контроля данных передаст управление
функции data_entry, и пользователь получит возможность произвести коррекцию
значений элементов данных. По завершении пользователем процесса обработки
записи система передаст управление прикладным функциям, которые непосредственно
будут обращаться в data_entry. Эти функции могут перезаписать откорректированную
запись в файл, очистить буфера данных и шаблоны путем обращения к функции
clear_template, переопределить защиту полей шаблона (а значит, и записи),
сняв защиту с поля "номер служащего" и установив защиту для
других полей, и повторить весь процесс обработки записи заново.
void field_help(FIELD *fld, int (*helpfn)())
Эта функция относится к макросам. Она
позволяет вам установить специальную help-функцию, которая будет вызываться
вместо стандартной help-функции. Эта функция используется для отдельных
полей шаблона, когда пользователю требуется получить по ним более полную
help-информацию, чем ему предоставлено текстом, отображаемым в окне.
В процессе ввода данных в окно у пользователя
могут возникнуть различные вопросы относительно как предназначения окна,
так и вида входных данных. Для получения ответов на свои вопросы пользователю
достаточно нажать на специально выделенную help-клавишу, которая инициирует
обращение к специальной help-функции. Функция data_entry передаст адрес
буфера накопления данных поля, что впоследствии даст возможность специальной
help-функции вернуть текущее значение в поле.
Эта отличительная черта функции field_help
используется, когда имеется список возможных значений, из которых пользователь
должен осуществлять выбор, но отображение всего списка в окне постоянно
нецелесообразно. Вы можете открыть еще одно окно для выдачи help-информации
с помощью вашей специальной help-функции, отобразить в нем весь список
возможных значений и затем использовать функцию get_selection (см. Главу
6) для обеспечения выбора пользователем нужного ему значения. Вы
также можете, как это делается в примере, который представлен в этой
главе ниже, записывать текущую дату в буферы накопления данных полей.
Вам предоставлена возможность осуществлять запросы к базе данных. Очевидно,
что при работе с записями файла "Служащий" базы данных ваша
help-функция может выдавать список номеров служащих и их фамилии, выбирая
эту информацию непосредственно из самого файла "Служащий".
int data_entry(WINDOW *wnd)
Эта функция обрабатывает вводимые в шаблон
данные. Пользователь может просмотреть весь шаблон со значениями всех
полей, выдавая на дисплей текущие значения данных из соответствующих
буферов накопления данных. Пользователь осуществляет ввод данных в поля
в той последовательности, в которой поля были установлены в шаблоне
с помощью функции establish_field, при этом позиция размещения поля
в шаблоне во внимание не принимается.
Поле, которое предназначено в данный
момент для ввода в него данных, отображается пользователю с учетом суммарного
пространства под вводимые данные, включая пунктуацию, и выделяется повышенной
яркостью в соответствии со значением параметра ACCENT, определяющим
цвет окна. Курсор устанавливается в первой позиции поля, и функция переходит
в режим ожидания ввода данных. Пользователь может начать ввод данных
или в текущее поле, или установить текущим другое поле, используя клавишу
<ВВОД>, клавишу табуляции <Таб> или клавиши управления курсором.
Клавиши <КЛЮЧ>, <Страница вверх> (<PgUp>), <Cтраница
вниз> (<PgDn>), <КОН> (<End>), перемещения курсора
в первую позицию первой строки (<Home>) и функциональные клавиши
(с <ПФ1> до <ПФ10>, исключая клавишу, за которой закреплен
вызов help-функции) предназначены для завершения процесса ввода данных
и возврата из функции data_entry, к которой перед этим было осуществлено
обращение, в точку вызова.
После ввода пользователем последнего
символа поля происходит выделение повышенной яркостью следующего поля
и перемещение к нему курсора. Перед этим функция data_entry обращается
к своим собственным функциям контроля данных и (если вы установили еще
и свои специальные функции контроля данных) к специальным функциям контроля.
Все эти функции должны в результате работы выдать значение ОК, в противном
случае функция data_entry не переместит курсор к следующему полю.
Если пользователь нажал клавишу, за которой
закреплен вызов help-функции, то будет осуществлен вызов специальной
help-функции (если она вами определена), предназначенной для текущего
поля, путем передачи ей адреса буфера накопления данных поля. Если help-функция
вами не создана и help-окно "привязано" к полю, то help-функции,
описанные в Главе 7, обеспечат выдачу на экран дисплея help-окна. Если
ни help-окно, ни help-функция не определены, то нажатие пользователем
help-клавиши не приведет ни к какому результату, если, конечно, программа
использует help-функции независимо от контекста вводимых в окно данных.
После того, как происходит возврат из функции data_entry в точку ее
вызова, все буферы полей содержат значения данных, которые ввел пользователь.
Эта функция возвращает управление в точку вызова при нажатии клавиш,
означающих конец ввода данных в шаблон. Таковыми являются клавиша <КЛЮЧ>,
клавиши листания страниц (<PgUp>, <PgDn>) или функциональные
клавиши, приводящие к завершению процесса ввода данных. Прикладное программное
обеспечение анализирует значение последней нажатой пользователем клавиши
для определения его намерений. Нажатие клавиши <КЛЮЧ>, например,
может означать "Игнорируй все внесенные мной изменения и верни
меня к предыдущему содержанию шаблона". Клавиши <Страница вверх>
(<PgUp>) и <Страница вниз> (<PgDn>) означают (как
определено в keys.h), что пользователь хочет перейти к следующей или
предыдущей записи базы данных. Когда пользователь завершает ввод данных,
то код завершения процесса ввода передается в программу, вызвавшую функцию
data_entry, для дальнейшего использования.
Исходный текст: entry.c
Листинг 8.1 представляет собой исходный
текст файла entry.c, который содержит библиотечные функции поддержки
оконного шаблон ввода данных.
Листинг 8.1: entry.c
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <alloc.h>
#include <mem.h>
#include <string.h>
#include "twindow.h"
#include "keys.h"
#define FIELDCHAR
'_'
int insert_mode = FALSE;
extern int helpkey;
void addfield(WINDOW *wnd, FIELD *fld);
void disp_field(WINDOW *wnd, char *bf, char *msk);
void data_value(WINDOW *wnd, FIELD *fld);
void insert_status(void);
int read_field(WINDOW *wnd, FIELD *fld);
void right_justify(char *s);
void right_justify_zero_fill(char *s);
int validate_date(char *s);
int endstroke(int c);
int spaces(char *c);
void init_template(WINDOW *wnd)
{
FIELD *fld, *fl;
fld = FHEAD;
while (fld) {
fl = fld->fnxt;
free(fld);
fld = fl;
}
FHEAD = NULL;
}
FIELD *establish_field(wnd, cl, rw, msk, bf,
ty)
WINDOW *wnd;
int rw;
int cl;
char *msk;
char *bf;
int ty;
{
FIELD *fld;
if ( (fld = malloc(sizeof(FIELD))) ==
NULL)
return NULL;
fld->fmask = msk;
fld->frow = rw;
fld->fcol = cl;
fld->fbuff = bf;
fld->ftype = ty;
fld->fprot = 0;
fld->fnxt = fld->fprv = NULL;
fld->fvalid =NULL;
fld->fhelp = NULL;
fld->fhwin = NULL;
fld->flx = fld->fly = 0;
addfield(wnd, fld);
return fld;
}
static void addfield(WINDOW *wnd, FIELD *fld)
{
if (FTAIL) {
fld->fprv = FTAIL;
FTAIL->fnxt = fld;
}
FTAIL = fld;
if (!FHEAD)
FHEAD = fld;
}
static void disp_field(WINDOW *wnd, char *bf,
char *msk)
{
while (*msk) {
wputchar(wnd, *msk != FIELDCHAR ? *msk : *bf++);
msk++;
}
}
static void data_value(WINDOW *wnd, FIELD
*fld)
{
wcursor(wnd, fld->fcol, fld->frow);
disp_field(wnd, fld->fbuff, fld->fmask);
}
void field_tally(WINDOW *wnd)
{
FIELD *fld;
fld = FHEAD;
while (fld != NULL) {
data_value(wnd, fld);
fld = fld->fnxt;
}
}
void field_window(FIELD *fld, char *hwin,
int x, int y)
{
fld->fhwin=hwin;
fld->flx = x;
fld->fly = y;
}
void clear_template(WINDOW *wnd)
{
FIELD *fld;
char *bf, *msk;
fld = FHEAD;
while (fld != NULL) {
bf = fld->fbuff;
msk = fld->fmask;
while (*msk) {
if (*msk == FIELDCHAR)
*bf++ = ' ';
msk++;
}
fld = fld->fnxt;
}
field_tally(wnd);
}
static void insert_status()
{
set_cursor_type(insert_mode ? 0x0106 : 0x0607);
}
static int read_field(WINDOW *wnd, FIELD *fld)
{
char *mask = fld->fmask, *buff = fld->fbuff;
int done = FALSE, c, column;
column = fld->fcol;
while (*mask != FIELDCHAR) {
column++;
mask++;
}
while (TRUE) {
wcursor(wnd, column, fld->frow);
c = get_char();
if (fld->ftype == 'A')
c = toupper(c);
clear_message();
switch (c) {
case '\b':
case BS:
if (buff == fld->fbuff) {
done = c == BS;
break;
}
--buff;
do {
--mask;
--column;
} while (*mask != FIELDCHAR);
if (c == BS)
break;
case DEL:
movmem(buff+1, buff, strlen(buff));
*(buff+strlen(buff)) = ' ';
wcursor(wnd, column, fld->frow);
disp_field(wnd, buff, mask);
break;
case FWD:
do {
column++;
mask++;
} while (*mask && *mask != FIELDCHAR);
buff++;
break;
case INS:
insert_mode ^= TRUE;
insert_status();
break;
case '.':
if (fld->ftype == 'C') {
if (*mask++ && *buff == ' ') {
*buff++ = '0';
if (*mask++ && *buff == ' ')
*buff++ = '0';
}
right_justify(fld->fbuff);
wcursor(wnd, fld->fcol, fld->frow);
disp_field(wnd, fld->fbuff, fld->fmask);
column = fld->fcol+strlen(fld->fmask)-2;
mask = fld->fmask+strlen(fld->fmask)-2;
buff = fld->fbuff+strlen(fld->fbuff)-2;
break;
}
default:
if (endstroke(c)) {
done = TRUE;
break;
}
if (toupper(fld->ftype)!='A'&&!isdigit(c)) {
error_message("Numbers only");
break;
}
if (insert_mode) {
movmem(buff, buff+1, strlen(buff)-1);
disp_field(wnd, buff, mask);
wcursor(wnd, column, fld->frow);
}
*buff++ = c;
wputchar(wnd, c);
do {
column++;
mask++;
} while (*mask && *mask != FIELDCHAR);
if (! *mask)
c = FWD;
break;
}
if (!*mask)
done = TRUE;
if (done) {
if (fld->ftype == 'D' &&
c != ESC &&
validate_date(fld->fbuff) !=OK)
return ERROR;
break;
}
}
if (c != ESC && toupper(fld->ftype) != 'A') {
if (fld->ftype == 'C') {
if (*mask++ && *buff == ' ') {
*buff++ = '0';
if (*mask++ && *buff == ' ')
*buff++ = '0';
}
}
if (fld->ftype == 'Z' || fld->ftype == 'D')
right_justify_zero_fill(fld->fbuff);
else
right_justify(fld->fbuff);
wcursor(wnd, fld->fcol, fld->frow);
disp_field(wnd, fld->fbuff, fld->fmask);
}
return c;
}
static int endstroke(int c)
{
switch (c) {
case '\r':
case '\n':
case '\t':
case ESC:
case F1:
case F2:
case F3:
case F4:
case F5:
case F6:
case F7:
case F8:
case F9:
case F10:
case PGUP:
case PGDN:
case HOME:
case END:
case UP:
case DN:
return TRUE;
default:
return FALSE;
}
}
static void right_justify(char *s)
{
int len;
len = strlen(s);
while (*s == ' ' || *s == '0' && len) {
len--;
*s++ = ' ';
}
if (len)
while (*(s+(len-1)) == ' ') {
movmem (s, s+1, len-1);
*s = ' ';
}
}
static void right_justify_zero_fill(char *s)
{
int len;
if (spaces(s))
return;
len = strlen(s);
while (*(s + len - 1) == ' ') {
movmem(s, s + 1, len-1);
*s = '0';
}
}
int spaces(char *c)
{
while (*c == ' ')
c++;
return !*c;
}
static int validate_date(char *s)
{
static int days [] =
{ 31,28,31,30,31,30,31,31,30,31,30,31 };
char date [7];
int mo;
strcpy(date, s);
if (spaces(date))
return OK;
days[1] = (atoi(date+4)%4) ? 28 : 29;
*(date + 4) = '\0';
mo = atoi(date+2);
*(date+2) = '\0';
if (mo && mo<13 && atoi(date) && atoi(date)<=days[mo-1])
return OK;
error_message("Invalid date");
return ERROR;
}
int data_entry(WINDOW *wnd)
{
FIELD *fld;
int exitcode, isvalid, done=FALSE, oldhelpkey=helpkey;
field_tally(wnd);
fld = FHEAD;
while (fld != NULL && done == FALSE)
{
set_help(fld->fhwin, fld->flx, fld->fly);
helpkey = (fld->fhelp) ? 0 : oldhelpkey;
wcursor(wnd, fld->fcol, fld->frow);
if (fld->fprot ==FALSE) {
reverse_video(wnd);
data_value(wnd, fld);
wcursor(wnd, fld->fcol, fld->frow);
exitcode = read_field(wnd, fld);
isvalid = (exitcode != ESC && fld->fvalid) ?
(*(fld->fvalid))(fld->fbuff) : OK;
}
else {
exitcode = FWD;
isvalid = OK;
}
if (isvalid == OK) {
normal_vileo(wnd);
data_value(wnd, fld);
switch (exitcode) {
case F1: if (fld->fhelp) {
(*(fld->fhelp))(fld->fbuff);
data_value(wnd, fld);
}
break;
case DN:
case '\r':
case '\t':
case FWD: fld = fld->fnxt;
if (fld == NULL)
fld = FHEAD;
break;
case UP:
case BS: fld = fld->fprv;
if (fld == NULL)
fld = FTAIL;
break;
default: done = endstroke(exitcode);
break;
}
}
}
helpkey = oldhelpkey;
return (exitcode);
}
void wprompt(WINDOW *wnd, int x, int y, char
*s)
{
wcursor(wnd, x, y);
wprintf(wnd, s);
}
Описание программы: entry.c
Макроопределение FIELDCHAR программы
entry.c идентифицирует специальные символы, используемые в символьной
маске поля при определении байтов данных. По сути дела, идентифицированные
с помощью FIELDCHAR символы не являются символами в смысле данных, так
как используются в маске. В качестве значения FIELDCHAR выступает символ
нижнего подчеркивания. Вы можете изменить определение FIELDCHAR, используя
другие символы.
Привязка полей к окнам осуществляется
с помощью двойного связного списка. Список начинается и заканчивается
в структуре WINDOW для поля. Каждое поле представлено посредством структуры
FIELD, которая размещается в памяти при установке поля.
Функция init_template используется для
инициализации окна с шаблоном ввода данных. Она выполняет трассировку
связного списка FIELD и освобождает некоторые размещенные ранее в памяти
структуры FIELD.
Функция establish_field размещает в памяти
и инициализирует буфер FIELD, используя передаваемые ей при обращении
значения или принимая значения передаваемых переменных по умолчанию.
Маска, позиция размещения поля, адрес буфера и тип данных поля передаются
функции при обращении к ней. Очередная структура FIELD добавляется в
конец соответствующего связного списка FIELD, специфицированного WINDOW.
Указатель на очередную структуру FIELD возвращается в точку вызова функции.
Функция addfield вызывается для добавления
структуры FIELD к связному списку, специфицированному WINDOW.
Функция disp_field используется для вывода
значений данных, введенных в поля, из буфера на экран дисплея. Маска
данных, передаваемая при обращении к функции, используется при отображении
содержимого поля. Обращение к функции содержит WINDOW, указатель буфера
поля и указатель маски. Подразумевается, что курсор в окне всегда устанавливается
на первую позицию первого поля окна. Символы, хранящиеся в буфере, отображаются
на экране вместе с символами пунктуации, составляющими маску поля.Функция
data_value предназначается для отображения текущего значения поля в
окне. Функция устанавливает курсор в окне в позицию первого символа
поля и вызывает функцию disp_field, передавая ей адрес WINDOW и адреса
буфера поля и маски.
Функция field_fally используется для
выдачи на экран значений всех полей шаблона. Она осуществляет просмотр
"сверху-вниз" связного списка FIELD и осуществляет обращение
к функции data_value для каждого поля шаблона.
Функция field_window предназначена для
установки оконной help-информации для поля. Указанные при обращении
к функции имя help-информации и координаты экрана для ее выдачи копируются
в специфицированную при вызове функции структуру FIELD.
Функция clear_template обрабатывает "сверху-вниз"
связной список FIELD для специфицированного окна. Функция преобразует
буфер каждого поля шаблона в пустую строку, заканчивающуюся нулевым
символом, используя маску поля для определения установленной при описании
поля длины. Когда все поля обработаны таким образом (почищены), функция
обращается к field_fally.
Программа ввода данных работает в режимах
вставки и замены символов. Клавиша <ВСТ> (<Ins>) используется
для переключения режимов работы программы, а переменная insert_mode
служит для индикации текущего режима работы. Функция insert_status изменяет
тип курсора в соответствии со значением переменной insert_mode. Режим
вставки обуславливает наличие квадратного курсора, а режим замены символов
- наличие курсора в виде нижнего подчеркивания. Для изменения формы
курсора используется функция set_cursor_type из библиотеки ibmpc.c (см.
Главу 4).
Функция read_field используется для чтения
введенных пользователем в поля данных. Она устанавливает курсор в начало
поля и считывает последовательность символов в нем. Два локальных указателя
используются для трассировки вводимых данных. Указатель mask позволяет
получить текущую позицию в маске, а указатель buff - текущую позицию
в буфере. При перемещении пользователем курсора, уничтожении или вводе
символов значение этих указателей корректируется.
При выполнении операции по уничтожению
символов используется функция Турбо Си movmem, которая используется
для сдвига символов в буфере, а также функция disp_field для отображения
результатов операции на экране.
Клавиша <ВСТ> (<Ins>) переключает
режимы Вставки/Замены.Если текущее поле включает в себя точку (.), то
оно содержит нули в двух крайних правых позициях (если другие значения
в них не указаны), выравнено вправо, а соответствующие указатели и курсор
устанавливаются на значащие позиции, предназначенные для указания пенни
(денежной единицы).
Если пользователь нажал клавишу, символизирующую
завершение ввода данных, то данная ситуация обрабатывается функцией
endstroke, и ввод данных завершается. В противном случае символ будет
записан в буфер. Если в данный момент обрабатывалось не алфавитно-цифровое
поле, то анализируется ситуация на предмет ввода пользователем числа.
Если было введено не число, то выдается сообщение об ошибке, и программа
не воспринимает введенные данные, требуя их повторного ввода. Если включен
режим вставки, то данные в буфере смещаются на одну позицию вправо,
а значение поля после вставки символа отображается с помощью функции
disp_field. При этом символ записывается в буфер и отображается на экране.
Совместное использование указателей маски и буфера позволяет "перескакивать"
через символы пунктуации. После ввода последнего символа в поле дальнейший
ввод данных в него прекращается.
После завершения ввода данные проверяются
на допустимость, даты и числа выравниваются, курсор устанавливается
в соответствующую позицию поля и содержимое полей отображается на экране.
Функция endstroke анализирует значение
клавиш и устанавливает клавиши, которые могут привести к завершению
процесса ввода данных.
Есть две функции, занимающиеся выравниванием
данных. Первая - right_justify, выравнивающая справа и заполняющая поле
пробелами. Вторая - right_justify_zero_fill, выравнивающая справа и
заполняющая поле нулями.
Функция spaces анализирует поле на наличие
всех пробелов.
Функция validate_date осуществляет проверку
даты.
Функция data_entry вызывается для обработки
всех полей шаблона. Функция вызывает field_fally для выдачи на экран
всех значений данных из соответствующих буферов полей. Затем функция
обрабатывает "сверху-вниз" связный список структур FIELD и
управляет вводом данных в каждом поле.
Функция set_help вызывается для осуществления
привязки оконной help-информации к конкретному полю. Поскольку значение
глобальной переменной helpkey определено заранее, то попытка получить
для этого поля специальную help-информацию закончится безуспешно, поскольку
программа data_entry будет перехватывать прерывание от клавиши help-функции.
Если поле не является защищенным, то
функция reverse_video вызывается каждый раз, когда необходимо получить
выделенное отображение поля в соответствии с конфигурацией цветов, определенной
параметром ACCENT. Для выдачи на экран значения текущего поля в акцентированном
режиме, определяемом параметром ACCENT, используется функция data_value,
а для ввода в программу введенных в поле пользователем данных применяется
функция read_field. Значением, возвращаемым функцией read_field, является
итоговая последовательность символов, суть которой - введенные в поле
данные. Если поле имеет свою специальную функцию контроля данных, то
вызывается эта функция.
При условии, что контроль данных по всем
параметрам прошел нормально, функция normal_video переводит окно в так
называемый контрольный режим отображения данных, цвет которого определяется
значением NORMAL, и функция data_value повторно выводит на экран значение
поля, но уже в NORMAL-цвете. Затем анализируется завершающая последовательность
нажатия клавиш. Если ввод данных прерван клавишей, закрепленной за вызовом
специальной help-функции, то осуществляется обращение к этой функции.
В связи с тем, что в процессе обработки полей реализована возможность
заменять буферизованные данные полей полностью, функция data_value снова
вызывается для повторного отображения поля после его обработки.
Завершающей клавишей в процессе ввода
данных могут быть либо клавиши движения вперед (стрелка вниз, ВВОД,
Таб), либо клавиши движения назад (стрелка вверх, стрелка влево). Следующее
поле для обработки выбирается из связного списка FIELD, специфицированного
параметром WINDOW, в зависимости от значений этих клавиш (вперед или
назад). При этом выбирается следующее или предыдущее поле, и ввод данных
продолжается.
Если последней была нажата одна из тех
клавиш, которые анализируются функцией endstroke и не являются аналогом
ранее встречающихся и анализируемых функцией последовательностей, то
ввод данных в оконный шаблон завершается, и функция data_entry возвращает
управление в точку вызова.
Пример: Ввод данных в
определенном порядке
Исходные файлы, представленные на листингах
8.2, 8.3 и 8.4, суть примеры использования шаблона ввода данных в окне.
Листинг 8.2, order.c, представляет собой текст главной функции (main),
которая осуществляет обращение к функции ordent.c, непосредственно реализующей
пример. Функция ordent.c будет в дальнейшем присутствовать при рассмотрении
примера меню в Главе 10 и резидентной утилиты
в Главе 12. Листинг 8.4 представляет собой make-файл
order.prj, используемый утилитой make Турбо Си для построения программы
примера.
Для выполнения программы примера введите
команду
C>order
Это окно является по сути дела "всплывающим",
то есть появляющимся в позиции курсора. Однако обратитесь к листингу
8.3, ordent.c, предварительно ознакомившись с описанием программы, приведенным
ниже. После того, как вы ознакомились с листингом, выполните программу.
Пример программы начинается с установки
окна и передачи ему значения заголовка и цвета. Окно отображается на
экране дисплея, и в шаблоне появляется приглашение к вводу данных. После
обращения к функции init_template осуществляется идентификация help-информации
для каждого поля (help-информация размещается в пределах окна), которая
поясняет назначение вводимых в каждое поле данных. Установка оконной
help-информации осуществляется путем обращения к функции field_window.
Каждое поле непосредственно устанавливается в шаблоне путем обращения
к функции establish_field. Последнее поле устанавливается в этом примере
как не использующее оконную help-информацию. Поэтому для данного поля
с помощью функции field_help идентифицируется специальная help-функция.
Обратите внимание, что одно поле шаблона - "state" - описано
как имеющее специальную функцию контроля данных, вводимых в него, на
достоверность. Эта специальная функция контроля данных специфицирована
в обращении к функции field_validate.
Программа обращается к функции clear_template
для получения пустого шаблона ввода данных, а затем - к функции data_entry,
позволяющей обрабатывать данные, вводимые в шаблон. По завершению работы
функции data_entry программа уничтожает окно. В реальной задаче в этой
точке программы вы должны предусмотреть обработку введенных данных.
В нашем примере после уничтожения окна программа завершает свою работу.
Во время выполнения программы обратите
внимание, каким образом вводимые символы помещаются в позиции определенных
полей, а также на управление курсором. Режим Вставки/Замены символов
управляется с помощью клавиши <Ins> и индуцируется соответствующей
формой курсора. Если курсор отображается как мерцающий символ подчеркивания
в обычном своем виде, то это означает, что данные вводятся в режиме
замены символов. После того, как вы нажмете клавишу <Ins>, курсор
примет форму мерцающего прямоугольника, и вы будете продолжать работу
в режиме вставки символов.
Нажмите клавишу <F1> для получения
справочной информации по каждому из полей шаблона. На рисунке 8.3 изображен
шаблон с некоторыми значениями полей в момент выдачи по запросу пользователя
оконной help-информации. Нажмите клавишу <F1> для поля "State"
и получите оконную help-информацию для него. Обратите внимание, что
в качестве help-информации выступает перечень допустимых значений поля
"State". Выберите одно из них и введите в качестве значения
поля. На примере этого поля внимательно изучите, как работает специальная
функция контроля данных, как она использует функцию error_message для
выдачи сообщений о вводе недопустимых данных. Специальная help-функция
для поля даты ("Date")
демонстрирует, как можно получить значение даты извне функции data_entry.
В данном примере для чтения текущей даты используется функция Турбо
Си getdate, после чего текущая дата записывается в соответствующий буфер
поля "Date".
Листинг 8.2: order.c
#include "twindow.h"
void ordent(void);
main()
{
load_help("tcprogs.hlp");
ordent();
}
Листинг 8.3:
ordent.c
#include <dos.h>
#include <stdio.h>
#include <string.h>
#include "twindow.h"
struct {
char name [26];
char addr [26];
char city [26];
char state [3];
char zip [6];
char amt [6];
char dt [7];
char phone [11];
} rcd;
char msk25 [] = "__________________________";
char mskamt [] = "____.____";
char mskdate [] = "___/___/___/";
char mskphone [] = "(___) ____-____";
#define mskst msk25+23
#define mskzip msk25+20
int validate_state(char *, int);
void help_date(char *);
void ordent()
{
WINDOW *wnd;
FIELD *fld;
wnd = establish_window(10,
5, 15, 50);
set_title(wnd, " Order Entry ");
set_colors(wnd, ALL, BLUE, AQUA, BRIGHT);
set_colors(wnd, ACCENT, WHITE, BLACK, DIM);
display_window(wnd);
wprompt(wnd, 5, 2, "Name:");
wprompt(wnd, 5, 3, "Address:");
wprompt(wnd, 5, 4, "City:");
wprompt(wnd, 5, 5, "State:");
wprompt(wnd, 18, 5, "Zip:");
wprompt(wnd, 5, 10, "Phone:");
wprompt(wnd, 5, 7, "Amount:");
wprompt(wnd, 5, 8, "Date:");
init_template(wnd);
fld = establish_field(wnd, 15, 2, msk25, rcd.name, 'a');
field_window(fld,"name ", 40, 1);
fld = establish_field(wnd, 15, 3, msk25, rcd.addr, 'a');
field_window(fld,"address ", 40, 2);
fld = establish_field(wnd, 15, 4, msk25, rcd.city, 'a');
field_window(fld, "address ", 40, 3);
fld = establish_field(wnd, 15, 5, mskst, rcd.state, 'A');
field_validate(fld, validate_state);
field_window(fld,"state ", 40, 4);
fld = establish_field(wnd, 23, 5, mskzip, rcd.zip, 'Z');
field_window(fld,"address ", 40, 4);
fld = establish_field(wnd,15,10,mskphone,rcd.phone, 'N');
field_window(fld,"phone ", 40, 9);
fld = establish_field(wnd, 15, 7, mskamt, rcd.amt, 'C');
field_window(fld,"amount ", 40, 8);
fld = establish_field(wnd, 15, 8, mskdate, rcd.dt, 'D');
field_help(fld, help_date);
clear_template(wnd);
data_entry(wnd);
delete_window(wnd);
}
int validate_state(bf, key)
char *bf;
{
static char *states [] =
{" ","VA","NC","SC","GA","FL",0};
char **st = states;
while (*st)
if (strcmp(*st++, bf) == 0)
return OK;
error_message("Недопустимое состояние ");
return ERROR;
}
void help_date(bf)
char *bf;
{
struct date dat;
getdate(&dat);
sprintf(bf, "%02d%02d%02d",
dat.da_day, dat.da_mon, dat.da_year % 100);
}
Листинг 8.4: order.prj
order
ordent (twindow.h)
entry (twindow.h, keys.h)
thelp (twindow.h, keys.h)
twindow (twindow.h, keys.h)
ibmpc.obj
Резюме
Оконная библиотека содержит сейчас
средства создания контекстно-зависимой help-информации и форматного
ввода данных. Эти средства вы можете с успехом использовать при создании
систем интерактивного ввода данных. Шаблон ввода данных поддерживает
фиксированные форматы вводимых данных. В Главе 9 в функции текстового
редактора добавлены новые, одна из них - возможность использования окна
для ввода и модификации произвольного текста.
|
|