Глава 13:Загадки AppGen (часть2)

Модификация FOXAPP.APP После того как предыдущая разработка пошла, я потерял покой. Как бы мне ни нравился тот маленький трюк, истинной моей любовью является поиск по неполному соответствию (Soft Seek). Если пользователь вводит SMIHT он, возможно, имеет ввиду SMITH. Я вывожу в виде меню наиболее подходящие соответствия с использованием для этого функции, код которой почти не зависит от конкретного приложения - я выбрасываю символы с конца, пока поиск не окажется успешным. Такой подход работает только с символьными полями на которые есть индекс и лучше всего он работает если индексирование проводилось с использованием функции UPPER(), что делает его не зависящим от регистра. Учитывая эти два ограничения мне удалось создать действительно полезную функцию!

Это требует серьезных изменений в коде FoxApp. FoxApp работает так, как считает нужным, и вам необходимо с этим считаться. Он, например, включает несколько .PRG-файлов, являющихся копиями программ, сгенерированных конструкторами меню и экранов. Так APPMENU.PRG включает жестко прошитое меню, появляющееся в сгенерированном FoxApp приложении, APPSRCH содержит функцию поиска, выводящую на экран маленькое окошко с возможными вариантами сортировки, полем ввода значения поиска и кнопкой < OK >. Для внесения изменений в FoxApp вам следует сделать следующее:


1) скопировать все .PRG проекта FoxApp в другой каталог;
2) модифицировать все, что вы сочтете нужным;
3) заново собрать FoxApp из проекта



Когда вы соберетесь запустить FoxApp убедитесь, что FoxPro знает, где располагается ваша измененная программа GENSCRN.FXP.

Я обнаружил два места куда следует внести изменения и пришлепнул их. Все изменения я вносил через менеджер проектов. Мне потребовалось несколько опытов, прежде чем я понял, что упомянутые в проекте .PRG-файлы обязательно должны быть в текущем каталоге. Ссылка на них в следующей команде:
BUILD PROJECT (m.projname) FROM (m.tmfname), ;
approc.prg, appmenu.prg, prtsetup.prg, getdest.prg, ;
getorder.prg, appabout.prg, appsrch.prg, (m.scxname)
BUILD APP (m.appname) FROM (m.projname)


Изменения кода для APPMENU.PRG и APPSRCH.PRG приведены в Листинге 13-4 и Листинге 13-5 соответственно. FoxApp генерирует .APP- файл так, что вы просто запустите программу, чтобы убедиться в ее работоспособности.

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

Наконец, я добавил в меню новую функцию JumpTo. Это требует внесения изменений в APPMENU.PRG - одну из программ, используемых FoxApp. Изменения приведены в Листинге 13-4.

Листинг 13-5 показывает как я заменил стандартный поиск, предлагаемый FoxPro, моим собственным, который требует меньшего количества нажатых клавиш и действует так, как мне хочется.

Как работает сгенерированный поиск по неполному соответствию

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

1) позволяет пользователям ввести только несколько первых символов ключевого слова, если они знают, что только малая часть записей базы данных соответствует критерию поиска;

2) позволяет находить неполные соответствия при неправильном вводе;

3) облегчает новичкам поиск до того как они научатся правильно строить запрос. Это было мое первое вторжение в код FoxApp и оказалось, что все много проще, чем я ожидал. Теперь, познав вкус успеха, я решил сотворить нечто более серьезное.

Еще одна задача для GenScrn: двуязычные экраны

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

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

Общий подход

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

Планы меняются

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

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

1) нельзя использовать те же экранные координаты строка, столбец> и

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

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

Технические требования

Координаты столбца для сообщений на обоих языках должны быть одинаковыми. Если вы напишете на экране с русскими сообщениями:

@ 5,5 SAY "Имя:"

и

@ 5,4 SAY "Name:"

для английской версии, то после вывода первым более длинного сообщения "Name" при переключении на русский перед сообщением "Имя" на экране останется буква N. Значит, вам нужно добавлять слева необходимое количество пробелов. Мне нравится выравнивать по правому краю сообщения и по левому - соответствующие поля ввода, например так:

Name:
Address:


Это дает возможность использовать начальные пробелы для выравнивания сообщений разной длины следующим образом:

@ 5,5 SAY "Имя:"
@ 5,4 SAY "Name:"


Первый прорыв

Очевидно, что начинать нужно с "конструктора экранов". К сожалению, имеется только одно место, куда можно поместить сообщения, и оно расположено непосредственно на экране. Конечно, вы можете щелкнуть на сообщении мышью (или несколько раз нажать на пробел) и получить возможность для записи комментария, но... Боже мой! Я никогда не использую поле комментариев. Почему бы не поместить в это поле сообщение на втором языке? В конце концов для программы GENSCRN это всего лишь текст.

Моя теорема неполноты

Второй проблемой было изменение размеров сообщений. Я просто не мог сообразить, как следует модифицировать Screen Builder, чтобы получить возможность варьировать размеры сообщений. Ответ был прост: я не могу этого сделать. Следовательно, не нужно даже пытаться.

В свое время я прочел книгу: Godel, Escher, Bach: An Eternal Golden Braid ("Бесконечная золотая коса"), основная мысль которой состояла в том, что не всегда можно попасть в некоторое место 'там' из некоторого места 'здесь'. Screen Builder не мог преодолеть то, во что я уперся - выровнять по длине пары сообщений. Ситуация напомнила мне головоломку типа "соедините точки линиями" или подобную, когда невозможно сообразить, как соединить 4 линиями 7 точек, потому что вы изначально полагаете, что не можете выйти за пределы участка, ограниченного точками. Измените правила - и головоломка решается просто.

Сводим все воедино

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

Это заняло примерно 10 минут. Программы FIXLEN1 и FIXLEN2 (Листинг 13-6 и Листинг 13-7) - две версии одной и той же программы. Первая использует BROWSE, тогда как вторая - выполняет обработку автоматически. Для использования программ подготовьте экран с сообщениями на втором языке, помещенными в поле comments для каждой текстовой строки. Затем выйдите из утилиты Screen Builder и запустите либо FIXLEN1, либо FIXLEN2. Если применяется версия, использующая BROWSE, можно наблюдать за процессом изменения размеров тех сообщений, которые имеют разную длину, по мере того как происходит переход от записи к записи. После того как вы убедились, что программа делает все как положено, самое время применить вторую программу, делающую то же самое быстрее.

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

Лихорадка AppGen

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

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

FUNCTION SWITCHER IF switch = 1 switch = 2 ELSE switch = 1 ENDIF

Затем для каждого сообщения:

IF switch = 1
@ <...> SAY [сообщение по-английски]
ELSE
@ <...> SAY [сообщение по-русски]
ENDIF


Кроме того, в самом начале мне было необходимо назначить управляющую клавишу:

ON KEY LABEL SHIFT+F2 DO SWITCHER

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

Из четырех модификаций две заключаются во вставке пары строк кода в сгенерированную программу. Единственным трудным местом было перемещение выражений с SAY. Оказалось, что GENSCRN содержит функцию по имени BUILDFMT, которая выводит на экран сообщения для SAY и GET. Она просматривает .SCX-файл и вызывает необходимую функцию в зависимости от числа в поле OBJTYPE (5 для текстового сообщения). Поэтому я закомментировал строки, которые выполняли генерацию выражений @ SAY.

Чтобы разместить их в ином месте требовался новый просмотр .SCX- файлов. Когда выяснилось его внутреннее имя, я составил фрагмент, приведенный в листинге 13-1. И оп-ля! Как обычно, модифицировать GENSCRN оказалось совсем нетрудно.

Четыре участка, где необходимы изменения исходного текста программы, приведены ниже. Как обычно, скопируйте GENSCRN.PRG в рабочий подкаталог, переименуйте его (я назвал свою программу LINGUIST.PRG) и добавьте строку:

_GENSCRN=C:\WORK\LINGUIST.PRG

в ваш CONFIG.FP. Разумеется, имена подкаталога и программы нужно указывать ваши собственные, а кроме того, следует убедиться, что переменная окружения foxprocfg не перебивает эту установку.

Необходимые изменения заключаются в следующем:


1) Добавьте код задания "горячей клавиши" для переключения режимов вывода во фрагмент SECTION 2 перед выражениями с GET;

2) Подавите все SAY в функции BUILD;

3) Выведите на дисплей текст сообщений для выбранного языка непосредственно перед отработкой команды READ;

4) Напишите фрагмент программы для добавления в завершающий участок (Cleanup Code) функции SWITCH вывода сообщений на соответствующем языке.

После выполнения программы снимите назначение ON KEY.


Эта версия обрабатывает только утилиты с одним экраном, но я и так уже на неделю просрочил время сдачи главы. Процедура GENCLEANUP записывает код, который размещается непосредственно после READ, активизирующей ваши GET. Сюда я и добавляю функцию SWITCHER. Для считывания соответствующих записей в .SCX-файле и генерации функции переключения я написал функцию GETSWITCH. Первая модификация приведена в Листинге 13-8.

Следующая модификация относится к GENSECT2, создающей код в конце установочного блока программы. Именно сюда записывается команда назначения клавиши переключения, как показано в Листинге 13-9.

В Листинге 13-10 приведена модификация, которую я сделал в BUILDFMT, процедуре создания кода инициализации индивидуальных GET. Я закомментировал код, который создает SAY(objtype=5), чтобы их можно было создать в отдельной процедуре при втором проходе .SCX-файла. Функция SWITCHER() вызывается один раз до команды READ для вывода начального значения текстовых сообщений. Иначе, текст не появится до первого нажатия Shift-F2, что может и вовсе не случиться. Функция показана в Листинге 13-11.

Код в Листинге 13-12 получен генерацией измененной программой GENSCRN.PRG.

И наконец, две процедуры для корректировки длины более короткого из пары сообщений. Они делают абсолютно одинаковую работу, только одна дает возможность просмотра, а вторая выполняет свое дело молча. Cм. Листинг 13-13, Листинг 13-14.