Приглашения к вводу в поле (Prompts) ----------------------------------------------------------------- Каждое поле может иметь свое специфичное приглашение к вводу в него данных, которое обычно содержит информацию, поясняющую пользователю, для чего предназначено данное поле (семантика поля) и какая именно информация должна быть в него введена. Такое приглашение представляет собой символьную строку, которую пользователь изменить не может. Вы должны сами специфицировать содержание этой строки и позицию ее размещения в окне. Приглашение к вводу данных должно иметь длину, не превышающую длину строки окна. Ввод данных ----------------------------------------------------------------- В процессе своей работы функция ввода данных обрабатывает поля шаблона в той последовательности, в которой вы осуществляли их описание (не принимая во внимание позиции их размещения в шаблоне), осуществляя при этом простейшую проверку допустимости находящихся в полях шаблона данных (правильность даты, поле должно содержать только цифры и т.д.). Вы можете также определить обычную (в смысле Си) функцию, которая будет осуществлять дальнейшую, возможно, сложную и разнообразную, проверку вводимых данных или определить функцию, которая будет выдавать в случае ввода недопустимых данных специальную help-информацию пользователю. В процессе ввода данных в шаблон используется выделение в соответствии с указанными атрибутами (ACCENT) цвета окна более ярким тоном полей, в которые предполагается ввод данных. При этом курсор на экране компьютера переходит в соответствующую позицию поля. В случае, если пользователь инициирует другой порядок ввода данных, переходя от поля к полю, то выделение следующего, выбранного пользователем поля ввода, и перемещение курсора также осуществляется программно. - 2 - Функции сбора данных ----------------------------------------------------------------- Эти функции, являющиеся библиотечными функциями, ваша программа так или иначе вызывает при использовании окна с шаблоном ввода данных. Запомните, что вы должны вначале установить окно, а лишь затем использовать эти функции. 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) указывает тип поля ввода данных. Различают следующие типы полей: А = алфавитно-цифровое - 3 - 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) При обращении к этой функции на экран дисплея выводятся - 4 - значения всех данных, хранящихся в буфере, для всех полей шаблона. Вы можете использовать эту функцию, когда, например, вы заполнили буфер значениями из записи базы данных. 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)()) Эта функция относится к макросам. Она использует адрес функции проверки допустимости вводимых значений (если таковая будет написана пользователем). Стандартные функции, - 5 - осуществляющие проверку допустимости вводимых в поля данных, обычно не осуществляют требуемой проверки на ошибочную ситуацию. По этой причине пользователь обычно сам пишет функцию, производящую контроль данных. Функция 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. Примите во внимание, что этот шаблон - 6 - содержит поля, образующие запись по одному служащему в файле базы данных. Шаблон ввода данных используется для ввода, отображения, поиска и изменения записей в файле. Перед началом ввода данных ваша программа может обратиться к функции field_protect для защиты всех полей, кроме поля "номер служащего". Затем вы можете обратиться к функции field_validate, передав ей указатель на обычные, разработанные вами функции контроля данных на допустимость. После того, как пользователь введет данные в поле "номер служащего", функция data_entry (будет описана позже) осуществит обращение к разработанной вами функции контроля данных, которая осуществит поиск и сравнение по ключу (номер служащего) нужной записи в базе данных. Функция контроля данных произведет загрузку соответствующих буферов накопления данных элементами данных из найденной записи базы данных, а затем обратится к функции field_fally для вывода значений элементов данных на экран. После этого будет вызвана функция field_protect для установки защиты для поля "номер служащего" и снятия защиты с остальных полей. В итоге функция контроля данных передаст управление функции data_entry, и пользователь получит возможность произвести коррекцию значений элементов данных. По завершении пользователем процесса обработки записи система передаст управление прикладным функциям, которые непосредственно будут обращаться в data_entry. Эти функции могут перезаписать откорректированную запись в файл, очистить буфера данных и шаблоны путем обращения к функции clear_template, переопределить защиту полей шаблона (а значит, и записи), сняв защиту с поля "номер служащего" и установив защиту для других полей, и повторить весь процесс обработки записи заново. +---------------------------------------------------------------+ | | | | | | | +------------Employee Record-----------+ | | | | | | | Employee #: 377 | | | | Name: Otis Cribblecoblis | | - 7 - | | Department: 2001 | | | | Salary: 15450.00 | | | | Date Hired: 01/02/55 | | | | SSN: 221-52-1234 | | | | | | | | | | | +--------------------------------------+ | | | | | | | +---------------------------------------------------------------+ Рис. 8.1. Пример шаблона ввода данных void field_help(FIELD *fld, int (*helpfn)()) Эта функция относится к макросам. Она позволяет вам установить специальную help-функцию, которая будет вызываться вместо стандартной help-функции. Эта функция используется для отдельных полей шаблона, когда пользователю требуется получить по ним более полную help-информацию, чем ему предоставлено текстом, отображаемым в окне. В процессе ввода данных в окно у пользователя могут возникнуть различные вопросы относительно как предназначения окна, так и вида входных данных. Для получения ответов на свои вопросы пользователю достаточно нажать на специально выделенную help-клавишу, которая инициирует обращение к специальной help-функции. Функция data_entry передаст адрес буфера накопления данных поля, что впоследствии даст возможность специальной help-функции вернуть текущее значение в поле. Эта отличительная черта функции field_help используется, когда имеется список возможных значений, из которых пользователь должен осуществлять выбор, но отображение всего списка в окне постоянно нецелесообразно. Вы можете открыть еще одно окно для - 8 - выдачи help-информации с помощью вашей специальной help-функции, отобразить в нем весь список возможных значений и затем использовать функцию get_selection (см. Главу 6) для обеспечения выбора пользователем нужного ему значения. Вы также можете, как это делается в примере, который представлен в этой главе ниже, записывать текущую дату в буферы накопления данных полей. Вам предоставлена возможность осуществлять запросы к базе данных. Очевидно, что при работе с записями файла "Служащий" базы данных ваша help-функция может выдавать список номеров служащих и их фамилии, выбирая эту информацию непосредственно из самого файла "Служащий". int data_entry(WINDOW *wnd) Эта функция обрабатывает вводимые в шаблон данные. Пользователь может просмотреть весь шаблон со значениями всех полей, выдавая на дисплей текущие значения данных из соответствующих буферов накопления данных. Пользователь осуществляет ввод данных в поля в той последовательности, в которой поля были установлены в шаблоне с помощью функции establish_field, при этом позиция размещения поля в шаблоне во внимание не принимается. Поле, которое предназначено в данный момент для ввода в него данных, отображается пользователю с учетом суммарного пространства под вводимые данные, включая пунктуацию, и выделяется повышенной яркостью в соответствии со значением параметра ACCENT, определяющим цвет окна. Курсор устанавливается в первой позиции поля, и функция переходит в режим ожидания ввода данных. Пользователь может начать ввод данных или в текущее поле, или установить текущим другое поле, используя клавишу <ВВОД>, клавишу табуляции <Таб> или клавиши управления курсором. Клавиши <КЛЮЧ>, <Страница вверх> (), (), <КОН> (), перемещения курсора в первую позицию первой строки () и функциональные клавиши (с <ПФ1> до <ПФ10>, исключая клавишу, за которой закреплен вызов help-функции) предназначены для завершения процесса ввода данных и возврата из функции - 9 - data_entry, к которой перед этим было осуществлено обращение, в точку вызова. После ввода пользователем последнего символа поля происходит выделение повышенной яркостью следующего поля и перемещение к нему курсора. Перед этим функция data_entry обращается к своим собственным функциям контроля данных и (если вы установили еще и свои специальные функции контроля данных) к специальным функциям контроля. Все эти функции должны в результате работы выдать значение ОК, в противном случае функция data_entry не переместит курсор к следующему полю. Если пользователь нажал клавишу, за которой закреплен вызов help-функции, то будет осуществлен вызов специальной help-функции (если она вами определена), предназначенной для текущего поля, путем передачи ей адреса буфера накопления данных поля. Если help-функция вами не создана и help-окно "привязано" к полю, то help-функции, описанные в Главе 7, обеспечат выдачу на экран дисплея help-окна. Если ни help-окно, ни help-функция не определены, то нажатие пользователем help-клавиши не приведет ни к какому результату, если, конечно, программа использует help-функции независимо от контекста вводимых в окно данных. После того, как происходит возврат из функции data_entry в точку ее вызова, все буферы полей содержат значения данных, которые ввел пользователь. Эта функция возвращает управление в точку вызова при нажатии клавиш, означающих конец ввода данных в шаблон. Таковыми являются клавиша <КЛЮЧ>, клавиши листания страниц (, ) или функциональные клавиши, приводящие к завершению процесса ввода данных. Прикладное программное обеспечение анализирует значение последней нажатой пользователем клавиши для определения его намерений. Нажатие клавиши <КЛЮЧ>, например, может означать "Игнорируй все внесенные мной изменения и верни меня к предыдущему содержанию шаблона". Клавиши <Страница вверх> () и <Страница вниз> () означают (как определено в keys.h), что пользователь хочет перейти к следующей или предыдущей записи базы данных. Когда пользователь завершает ввод данных, то код завершения процесса ввода передается в - 10 - программу, вызвавшую функцию data_entry, для дальнейшего использования. Исходный текст: entry.c ----------------------------------------------------------------- Листинг 8.1 представляет собой исходный текст файла entry.c, который содержит библиотечные функции поддержки оконного шаблона ввода данных. Листинг 8.1: entry.c /*----------------entry.c----------------*/ #include #include #include #include #include #include #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); - 11 - 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; - 12 - 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); } - 13 - /*----------отображение всех полей в окне---------------*/ void field_tally(WINDOW *wnd) { FIELD *fld; fld = FHEAD; while (fld != NULL) { data_value(wnd, fld); fld = fld->fnxt; } } /*-----------установка help-окон для полей--------------*/ 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; - 14 - } 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; - 15 - --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: - 16 - 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'; - 17 - 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: - 18 - 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'; } } - 19 - /*---------------контроль пробелов-------------*/ 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; - 20 - /*--накопление данных, поступающих с клавиатуры на экране --*/ 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; - 21 - 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. - 22 - Функция 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 обрабатывает "сверху-вниз" связной - 23 - список FIELD для специфицированного окна. Функция преобразует буфер каждого поля шаблона в пустую строку, заканчивающуюся нулевым символом, используя маску поля для определения установленной при описании поля длины. Когда все поля обработаны таким образом (почищены), функция обращается к field_fally. Программа ввода данных работает в режимах вставки и замены символов. Клавиша <ВСТ> () используется для переключения режимов работы программы, а переменная insert_mode служит для индикации текущего режима работы. Функция insert_status изменяет тип курсора в соответствии со значением переменной insert_mode. Режим вставки обуславливает наличие квадратного курсора, а режим замены символов - наличие курсора в виде нижнего подчеркивания. Для изменения формы курсора используется функция set_cursor_type из библиотеки ibmpc.c (см. Главу 4). Функция read_field используется для чтения введенных пользователем в поля данных. Она устанавливает курсор в начало поля и считывает последовательность символов в нем. Два локальных указателя используются для трассировки вводимых данных. Указатель mask позволяет получить текущую позицию в маске, а указатель buff - текущую позицию в буфере. При перемещении пользователем курсора, уничтожении или вводе символов значение этих указателей корректируется. При выполнении операции по уничтожению символов используется функция Турбо Си movmem, которая используется для сдвига символов в буфере, а также функция disp_field для отображения результатов операции на экране. Клавиша <ВСТ> () переключает режимы Вставки/Замены. Если текущее поле включает в себя точку (.), то оно содержит нули в двух крайних правых позициях (если другие значения в них не указаны), выравнено вправо, а соответствующие указатели и курсор устанавливаются на значащие позиции, предназначенные для указания пенни (денежной единицы). - 24 - Если пользователь нажал клавишу, символизирующую завершение ввода данных, то данная ситуация обрабатывается функцией endstroke, и ввод данных завершается. В противном случае символ будет записан в буфер. Если в данный момент обрабатывалось не алфавитно-цифровое поле, то анализируется ситуация на предмет ввода пользователем числа. Если было введено не число, то выдается сообщение об ошибке, и программа не воспринимает введенные данные, требуя их повторного ввода. Если включен режим вставки, то данные в буфере смещаются на одну позицию вправо, а значение поля после вставки символа отображается с помощью функции disp_field. При этом символ записывается в буфер и отображается на экране. Совместное использование указателей маски и буфера позволяет "перескакивать" через символы пунктуации. После ввода последнего символа в поле дальнейший ввод данных в него прекращается. После завершения ввода данные проверяются на допустимость, даты и числа выравниваются, курсор устанавливается в соответствующую позицию поля и содержимое полей отображается на экране. Функция endstroke анализирует значение клавиш и устанавливает клавиши, которые могут привести к завершению процесса ввода данных. Есть две функции, занимающиеся выравниванием данных. Первая - right_justify, выравнивающая справа и заполняющая поле пробелами. Вторая - right_justify_zero_fill, выравнивающая справа и заполняющая поле нулями. Функция spaces анализирует поле на наличие всех пробелов. Функция validate_date осуществляет проверку даты. Функция data_entry вызывается для обработки всех полей шаблона. Функция вызывает field_fally для выдачи на экран всех - 25 - значений данных из соответствующих буферов полей. Затем функция обрабатывает "сверху-вниз" связный список структур 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 снова вызывается для повторного отображения поля после его обработки. Завершающей клавишей в процессе ввода данных могут быть либо - 26 - клавиши движения вперед (стрелка вниз, ВВОД, Таб), либо клавиши движения назад (стрелка вверх, стрелка влево). Следующее поле для обработки выбирается из связного списка 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.2, является по сути дела "всплывающим", то есть появляющимся в позиции курсора. Однако обратитесь к листингу 8.3, ordent.c, предварительно ознакомившись с описанием программы, приведенным ниже. После того, как вы ознакомились с листингом, выполните программу. - 27 - +---------------------------------------------------------------+ | | | C> | | | | +------------Order Entry-------------+ | | | | | | | Name: | | | | Address: | | | | City: | | | | State: Zip: | | | | | | | | Amount: . | | | | Date: / / | | | | | | | | Phone: ( ) - | | | | | | | +------------------------------------+ | | | +---------------------------------------------------------------+ Рис.8.2. Образец ввода шаблона ввода данных Пример программы начинается с установки окна и передачи ему значения заголовка и цвета. Окно отображается на экране дисплея, и в шаблоне появляется приглашение к вводу данных. После обращения к функции init_template осуществляется идентификация help-информации для каждого поля (help-информация размещается в пределах окна), которая поясняет назначение вводимых в каждое поле данных. Установка оконной help-информации осуществляется путем обращения к функции field_window. Каждое поле непосредственно устанавливается в шаблоне путем обращения к функции establish_field. Последнее поле устанавливается в этом примере как не использующее оконную help-информацию. Поэтому для данного поля с помощью функции field_help идентифицируется специальная help-функция. Обратите внимание, что одно поле - 28 - шаблона - "state" - описано как имеющее специальную функцию контроля данных, вводимых в него, на достоверность. Эта специальная функция контроля данных специфицирована в обращении к функции field_validate. Программа обращается к функции clear_template для получения пустого шаблона ввода данных, а затем - к функции data_entry, позволяющей обрабатывать данные, вводимые в шаблон. По завершению работы функции data_entry программа уничтожает окно. В реальной задаче в этой точке программы вы должны предусмотреть обработку введенных данных. В нашем примере после уничтожения окна программа завершает свою работу. Во время выполнения программы обратите внимание, каким образом вводимые символы помещаются в позиции определенных полей, а также на управление курсором. Режим Вставки/Замены символов управляется с помощью клавиши и индуцируется соответствующей формой курсора. Если курсор отображается как мерцающий символ подчеркивания в обычном своем виде, то это означает, что данные вводятся в режиме замены символов. После того, как вы нажмете клавишу , курсор примет форму мерцающего прямоугольника, и вы будете продолжать работу в режиме вставки символов. Нажмите клавишу для получения справочной информации по каждому из полей шаблона. На рисунке 8.3 изображен шаблон с некоторыми значениями полей в момент выдачи по запросу пользователя оконной help-информации. Нажмите клавишу для поля "State" и получите оконную help-информацию для него. Обратите внимание, что в качестве help-информации выступает перечень допустимых значений поля "State". Выберите одно из них и введите в качестве значения поля. На примере этого поля внимательно изучите, как работает специальная функция контроля данных, как она использует функцию error_message для выдачи сообщений о вводе недопустимых данных. Рисунок 8.4 наглядно демонстрирует именно случай неправильного ввода данных в поле "State". Специальная help-функция для поля даты ("Date") - 29 - демонстрирует, как можно получить значение даты извне функции data_entry. В данном примере для чтения текущей даты используется функция Турбо Си getdate, после чего текущая дата записывается в соответствующий буфер поля "Date". +---------------------------------------------------------------+ | | | C> +----------------------------+ | | | Enter the address of the | | | | person who is placing the | | | +------------ Order entry| order. | | | | | [Help] to return | | | | +---------------------+------+ | | | Name: Clifford Brown | | | | Address: 123 Main Street | | | | City: Springfield | | | | State: VA Zip: 21333 | | | | | | | | Amount: 23.40 | | | | Date: 26/07/87 | | | | | | | | Phone: (202) 321-3211 | | | | | | | +----------------------------------------------+ | | | +---------------------------------------------------------------+ Рис. 8.3. Образец ввода шаблона ввода данных с данными и help-информацией +---------------------------------------------------------------+ | | | C> | | | | | - 30 - | +-------------Order Entry-----------------+ | | | | | | | Name: Clifford Brown | | | | Address: 123 Main Street | | | | City: Springfield | | | | State: TX Zip: 21333 | | | | | | | | Amount: 23.40 | | | | Date: 26/07/87 | | | | | | | | Phone: (202) 321-3211 | | | | | | | +-----------------------------------------+ | | | | +-----ERROR!-----+ | | | | | | | Invalid State | | | +----------------+ | | | +---------------------------------------------------------------+ Рис. 8.4. Проверка корректности ввода данных Листинг 8.2: order.c /*-----------order.c---------*/ #include "twindow.h" void ordent(void); main() { load_help("tcprogs.hlp"); ordent(); } - 31 - Листинг 8.3: ordent.c /*-----------------ordent.c----------------*/ #include #include #include #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); - 32 - 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; - 33 - { 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 - 34 - Резюме ----------------------------------------------------------------- Оконная библиотека содержит сейчас средства создания контекстно-зависимой help-информации и форматного ввода данных. Эти средства вы можете с успехом использовать при создании систем интерактивного ввода данных. Шаблон ввода данных поддерживает фиксированные форматы вводимых данных. В Главе 9 в функции текстового редактора добавлены новые, одна из них - возможность использования окна для ввода и модификации произвольного текста. ГЛАВА 9 ------- Оконный текстовый редактор ----------------------------------------------------------------- В Главе 8 обсуждалось применение видеоокон для использования ввода данных в поля формо-ориентированного шаблона ввода данных. Естественно, что не все поля данных имеют заранее определенный, фиксированный формат. Многие поля и файлы состоят из текстов свободной, неопределенной формы и последовательности, например, слова в книге или комментарии к программе. Текстовые процессоры и редакторы текстов позволяют вам строить текстовые файлы, игнорируя формат исходных данных (текста), однако, если вы создаете действительно интегрированную систему , то доступ к функциям ввода текста в вашу программу и его редактирование может вам понадобиться непосредственно из вашей прикладной программы. Эта глава является предварительным описанием оконного - 35 - текстового редактора, который использует видеоокна для ввода и модификации текста точно так же, как большинство программ редактирования текста и текстовых процессоров. Эта отличительная черта оконного редактора используется при создании программных систем, в которых требуется ввод текста произвольной формы. Пакет Sidekick является примером системы, в которой имеется сходный с рассматриваемым в этой главе редактор текстов. Пример программы, представленный в этой главе, вполне может использоваться в качестве редактора текстов в системах типа Sidekick. Оконный текстовый редактор может также применяться в системах ввода данных, когда требуется ввести в качестве данных текст произвольной формы, занимающий более одной строки. Прикладная программа типа программы ввода данных в заданном порядке с помощью шаблона ввода данных из Главы 8 может потребовать ввода поля, содержащего описательный текст. Это требование можно легко реализовать, применяя оконный текстовый редактор. Многие базы данных оперируют с данными, характеризующимися наличием поясняющего их текста, различных описаний или определенной терминологии предметной области. При использовании шаблонов ввода данных поля данных такого типа могут с успехом обрабатываться оконным редактором. вы также можете использовать текстовый редактор для подготовки текстов help-окон в вашей программе путем обработки файла описаний help-окон, описанного в Главе 7. Для использования текстового редактора программа вначале устанавливает окно, а затем резервирует буфер, в котором будут храниться и редактироваться данные, вводимые пользователем. Текстовый буфер редактора представляет собой массив, резервирующий память, достаточную для хранения фиксированного числа строк, каждая из которых равна ширине области окна, отводимой для ввода и редактирования текста. Например, если вы установили ширину окна в 42 символа, то область текста будет иметь ширину 40 символов (рамка окна занимает 2 позиции). Если вы в этом случае отвели под буфер 4000 символов (длина массива), то редактор сможет обрабатывать одновременно только 100 строк текста, так как буфер может содержать не более 100 строк текста по 40 символов в строке. При вводе пользователем текста он - 36 - накапливается в буфере без специальных символов новой строки (\n) и табуляции (\t). Если текстовый буфер уже содержит текст, который был занесен в него при первом обращении к редактору, то этот старый текст отображается на экране дисплея в виде строк фиксированной длины в соответствии с описанием окна. Команды тестового редактора ----------------------------------------------------------------- Текстовый редактор содержит полный набор команд редактирования. Команды сведены в один перечень и могут быть отображены на экране дисплея в виде, представленном на рисунке 9.1. В прошлой главе была рассмотрена программа, которая использовала help-окно для выдачи различной информации. Help-окно отображается при нажатии клавиши . Точно такой же прием используется для отображения набора команд редактора. Большинство из команд редактирования закреплено за функциональными клавишами, остальные используют комбинации клавиш или с другими клавишами. Ниже будут рассмотрены все команды оконного текстового редактора. +---------------------------------------------------------------+ |+---------------------------------------------------------+ | ||------Cursor Movement--------------Page Movement-------- | | ||arrows = move text cursor Ctrl-Home = Beginning of File | | ||Ctrl-T = Top of Window Ctrl-End = End of File | | ||Ctrl-B = Bottom of Window PgUp = Previous Page | | ||Ctrl ->= Next Word PgDn = Next Page | | ||Ctrl <-= Previons Word ++ | ||Home = Beginning of Line --------Editor control------- || | ||End = End of Line Scroll Lock = No Auto Reform || | || || | || || | ||-------Block controls---------------Edit commands------- || | - 37 - ||F4 = Form Paragraph F2 or Esc = Done || | ||F5 = Mark Block Beginning F3 = Erase File || | ||F6 = Mark Block End Ins = Togle Insert Mode || | ||F7 = Move Block Del = Delete Char || | ||F8 = Copy Block <-- = Rubout || | ||F9 = Delete Block Ctrl-D = Delete Word || | ||F10 = Unmark Block Alt-D = Delete Line ++ | || [Help] to return | | |+---------------------------------------------------------+ | | | | | | | +---------------------------------------------------------------+ Рис. 9.1. Команды текстового редактора Управление курсором ----------------------------------------------------------------- Используя клавиши управления курсором, его можно перемещать по всему экрану. Если вы переместили курсор в начало или в конец экрана и продолжаете нажимать клавишу перемещения курсора вверх (или вниз), то тем самым вы инициируете скроллинг текста, который будет продолжаться до достижения начала (конца) буфера данных. - и перемещают буфер в начало и конец экрана; - перемещает курсор в начало следующего слова в буфере хранения текста; - перемещает курсор в начало предыдущего слова в буфере хранения текста; - <курсор в начало экрана> позиционирует курсор в начало текущей строки; - 38 - - <КОН> позиционирует курсор в конец текущей строки; - <ТАБ> перемещает курсор к следующей позиции табуляции; - <РЕГИСТР/ТАБ> перемещает курсор к предыдущей позиции табуляции. Постраничная работа ----------------------------------------------------------------- - <Страница вверх> и <Страница вниз> перемещает текст на страницу вперед или назад; - устанавливает текст на первую страницу, позиционируя курсор в первой позиции первой строки текста; - устанавливает текст на последнюю страницу в буфере и позиционирует курсор в первой позиции строки, которая следует за последней строкой текста в буфере. Команды работы с блоками текста ----------------------------------------------------------------- Текстовый редактор имеет в своем составе команды работы с блоками текста. Эти команды позволяют оперировать с блоками текстов, которые определены ограничительными линиями. - формирует параграф из текста, помеченного как блок; - помечает первую строку блока. Строка текста, в которой находится курсор, становится первой строкой блока. Текст, образующий блок, отображается в цвете, определяемом значением параметра ACCENT для данного - 39 - окна; - помечает последнюю строку блока текста; - перемещает весь блок текста к строке текста, в которой расположен в данный момент курсор. Перемещение блока происходит без его разрушения; пространство для размещения блока выделяется автоматически; - копирует блок к текущей строке текста. Отличие этой команды от предыдущей в том, что настоящая команда не приводит к разрушению блока-оригинала на его прежнем месторасположении в тексте. - уничтожает блок. Строки текста, следующие за удаленным блоком, перемещаются вверх, заполняя освободившееся пространство в тексте; - отменяет маркировку фрагмента текста как блока. Команды редактирования ----------------------------------------------------------------- После того, как вы ввели текст и осуществили перемещение курсора, редактор автоматически переформатирует текущий параграф. Редактор определяет параграф как группу строк текста, начинающуюся с первой строки, состоящей из пробелов. Переформатирование параграфов может подавляться путем нажатия перечисленных ниже клавиш в режиме (Scroll Lock): - уничтожает весь текст, хранящийся в буфере "Команда". Команда требует подтверждения; - переключает режимы работы редактора Вставка/Замена, - 40 - а также изменяет форму курсора, индицирующего режим работы редактор; - уничтожает символ, находящийся после курсора в тексте, перемещая весь текст в строке на одну позицию влево; - Забой (длинная стрелка в правом верхнем углу клавиатуры) уничтожает символ, находящийся левее курсора, перемещая сам курсор и весь следующий за ним текст строки на одну позицию влево; - () уничтожает слово, перед которым позицирован курсор; - уничтожает строку текста, в которой был предварительно позицирован курсор; - или приводит к выходу из текстового редактора в точку его вызова из главной программы. Функция, реализующая текстовый редактор ----------------------------------------------------------------- Текстовый редактор реализован в виде отдельной функции, которую может вызвать ваша программа. Для использования этой функции вы должны вначале организовать окно, в которое будут вводиться и в котором будут обрабатываться текстовые данные. void text_editor (WINDOW *wnd, char *bf, unsigned bsize) Эта функция обрабатывает текстовые данные, вводимые в окно с помощью текстового редактора. Указатель wnd специфицирует предварительно установленное вами окно. Указатель bf определяет текстовый буфер, а целое число bsize специфицирует размер буфера. - 41 - Количество строк в буфере является функцией, зависящей от размера буфера и размеров (ширины) окна, которые устанавливаются, когда вы организуете окно. При вызове функции она отображает текст, начиная с первой страницы из текстового буфера, и позволяет как ввод текстовых данных, так и ввод команд редактирования и обработки текста с клавиатуры. При выходе из текстового редактора буфер содержит введенный или модифицированный пользователем текст. Исходный листинг: editor.c ----------------------------------------------------------------- Листинг 9.1 содержит исходный текст оконного текстового редактора. Листинг 9.1: editor.c /* ------------------------- editor.c ------------------------ */ #include #include #include #include #include #include "twindow.h" #include "keys.h" #define TRUE 1 #define FALSE 0 #define TAB 4 #define NEXTTAB (TAB-(x%TAB)) #define LASTTAB (((wwd-1)/TAB)*TAB) #define PREVTAB (((x-1)%TAB)+1) #define curr(x,y) (bfptr+(y)*wwd+(x)) #define lineno(y) ((int)(bfptr-topptr)/wwd+(y)) - 42 - extern int VSG; int last_x, last_y; static int wht; static int wwd; static int wsz; static char *topptr; static char *bfptr; static char *lstptr; static int lines; static char *endptr; static int blkbeg; static int blkend; static int inserting; static WINDOW *wnd; static int do_display_text =1; /* ---------- local function prototypes ----------- */ void erase_buffer(int *x, int*y); int lastword(int x, int y); void last_char(int *x, int *y); void test_para(int x, int y); int traling_spaces(int); int first_wordlen(int y); void paraform(int x, int y); int blankline(int line); void delete_word(int x, int y); void delete_line(int y); void delete_block(void); void copy_block(int y); void move_block(int y); void mvblock(int y, int moving); void findlast(void); void find_end(int *x, int *y); void carrtn(int *x, int *y, int insert); void backspace(int *x, int *y); void fore_word(int *x, int *y, char *bf); - 43 - int spaceup(int *x, int *y, char **bf); void back_word(int *x, int *y, char *bf); int spacedn(int *x, int *y, char **bf); void forward(int *x, int *y); int downward(int *y); void upward(int *y); void display_text(void); void disp_line(int y); void insert_line(void); /* ----- Process text entry for a window. ----- */ void text_editor(WINDOW *wnd1, char *bf, unsigned bsize) { char *b, *buff; int depart = FALSE, i, c; int x, y, svx, svlw, tx, tabctr = 0; wnd = wnd1; wht = HEIGHT-2; wwd = WIDTH-2; topptr = bfptr = bf; lines = bsize / wwd; endptr = bf + wwd * lines; blkbeg = 0; blkend = 0; inserting = FALSE; x = 0; y = 0; display_text(); /* --------- read in text from the keyboard ---------- */ findlast(); while (TRUE) { last_x = COL + 1 + x; last_y = ROW + 1 + y; cursor(last_x, last_y); buff = curr(x, y); if (tabctr) { - 44 - --tabctr; c = ' '; } else { c = get_char(); clear_message(); } switch (c) { case '\r': carrtn(&x, &y, inserting); break; case DN: downward(&y); break; case PGUP: y = 0; for (i = 0; i < wht; i++) upward(&y); break; case PGDN: y = HEIGHT - 2; for (i = 0; i < wht; i++) downward(&y); y = 0; break; case '\t': if (x + NEXTTAB < wwd) { if (inserting) tabctr = NEXTTAB; else x += NEXTTAB; } else carrtn(&x, &y, inserting); break; case SHIFT_HT: if (x < TAB ) { upward(&y); x = LASTTAB; } else x -= PREVTAB; - 45 - break; case CTRL_FWD: fore_word(&x, &y, buff); break; case CTRL_BS: back_word(&x, &y, buff); break; case CTRL_B: y = wht - 1; break; case CTRL_T: y = 0; break; case CTRL_HOME: x = y = 0; bfptr = topptr; display_text(); break; case HOME: x = 0; break; case CTRL_END: find_end(&x, &y); display_text(); break; case END: last_char(&x, &y); break; case UP: upward(&y); break; case F2: case ESC: depart = TRUE; break; case '\b': case BS: if (curr(x, y) == topptr) - 46 - break; backspace(&x, &y); if (x == wwd - 1) last_char(&x, &y); if (c == BS) break; buff = curr(x, y); case DEL: movmem(buff+1, buff, wwd-1-x); *(buff+wwd-1-x) = ' '; disp_line(y); test_para(x+1, y); break; case ALT_D: delete_line(y); break; case INS: inserting ^= TRUE; insert_line(); break; case F3: erase_buffer(&x, &y); break; case F4: paraform(0, y); break; case F5: blkbeg = lineno(y) + 1; if (blkbeg > blkend) blkend = lines; display_text(); break; case F6: blkbeg = lineno(y) + 1; if (blkbeg < blkend) blkend = 1; display_text(); break; case F7: move_block(y); break; case F8: copy_block(y); break; case F9: delete_block(); break; - 47 - case F10: blkbeg = blkend = 0; display_text(); break; case FWD: forward(&x, &y); break; default: if (!isprint(c)) break; if (curr(x, y) == endptr-1 || (lineno(y)+1 >= lines && inserting && *curr(wwd-2, y) != ' ')) { error_message(" End of Buffer "); break; } if (inserting) { buff = curr(x, y); movmem(buff, buff + 1, wwd-1-x); } buff = curr(x, y); if (buff < endptr) { if (buff >= lstptr) lstptr = buff + 1; *buff = c; disp_line(y); } buff = curr(wwd-1, y); if (endptr && *buff != ' ') { for (b = buff+1; b < endptr; b++) if (*b==' ' && *(b + 1)==' ') break; movmem(buff+1, buff+2, b-buff-1); *(buff+1) = ' '; svx = x; svlw = lastword(x, y); x = wwd-1; if (*(buff-1) != ' ') back_word(&x, &y, buff); tx = x; - 48 - carrtn(&x, &y, TRUE); if (svlw) x = svx-tx; else { x = svx; --y; } } forward(&x, &y); break; } if (depart) break; } inserting = FALSE; insert_line(); } /* -------- erase the buffer ---------------- */ static void erase_buffer(int *x, int *y) { int c = 0; WINDOW *sur; sur = establish_window(28, 11, 4, 24); set_colors(sur, ALL, RED, YELLOW, BRIGHT); display_window(sur); wprintf(sur, " Erase text window\n Are you sure? (y/n)"); while (c != 'y' && c != 'n') { c = get_char(); c = tolower(c); if (c == 'y') { lstptr = bfptr =topptr; *x = *y = 0; setmem(bfptr, lines * wwd, ' '); blkbeg = blkend = 0; display_text(); - 49 - } } delete_window(sur); } /* ----- see if a word is the last word on the line ------ */ static int lastword(int x, int y) { char *bf = curr(x, y); while (x++ < wwd-1) if (*bf++ == ' ') return 0; return 1; } /* str 188 */ /* --- go to last displayable character on the line --- */ static void last_char(int *x, int *y) { char *bf; *x = wwd-1; bf = curr(0, *y); while(*x && *(bf + *x) == ' ') --(*x); if (*x && *x < wwd-1) (*x)++; } /* ----- test to see if paragraph should be reformed ----- */ static void test_para(int x, int y) { int ts, fw; if(!scroll_lock() && y < lines) { ts= trailing_spaces(y); fw = fierst_wordlen(y+1); - 50 - if (fw && ts > fw) paraform(x, y); } } /* ---- count the trailing spaces on a line ----- */ static int trailing_spaces(int y) { int x = wwd-1 ,ct = 0; char *bf=curr(0, y); while (x >= 0) { if (*(bf + x) != ' ') break; --x; ct++; } return ct; } /* ----- count the length of the first word on a line --- */ static int fiest_wordlen(int y) { int ct = 0, x = 0; char *bf = curr(0, y); while (x < wwd-1 && *(bf+x) == ' ') x++; while (x+ct < wwd-1 && *(bf+x+ct) == ' ') ct++; return ct; } /*----------------- form a paragraph -------------------- */ static void paraform(int x, int y) { char *cp1, *cp2, *cpend, *svcp; - 51 - int x1; if (blankline(lineno(y)+1)) return; if (!blkbeg) { blkbeg = blkend = lineno(y)+1; blkend++; while(blkend < lines) { if (blankline(blkend)) break; blkend++; } --blkend; } if(lineno(y) != blkend-1 ) x=0; x1=x; cp1=cp2=topptr + (blkend-1) * wwd + x; cpend = topptr + blkend * wwd; while(cp2 < cpend){ while(*cp2 == ' ' && cp2 < cpend) cp2++; if(cp2 == cpend) break; /* at a word */ while(*cp2 != ' ' && cp2 < cpend) { if(x1 >= wwd-1) { /* wrap the word */ svcp = cp1 + (wwd - 1); while(*--cp1 != ' ') { *cp1 = ' '; --cp2; } x1 = 0; blkbeg++; cp1 = svcp; - 52 - } *cp1++ = *cp2++; x1++; } if(cp2 < cpend) { *cp1++ = ' '; x1++; } } while(cp1 < cpend) *cp1++ = ' '; blkbeg++; if(blkbeg <= blkend) delete_block(); blkbeg =blkend =0; display_text(); findlast(); }