Автор: Иванов Борис
Публикуется без редактирования и должной проверки. Все замеченные замечания 🙂 слать в редакцию.
…мне почему-то показалось что с выходом с7 стало как-то больше молодежи на форуме, подумал что будет полезно. Плюс статиститика опроса на сайте https://www.clarionlife.net показывает что есть целый 1 человек младше 18, может ему будет полезно.
Просто статей как-то совсем не добавляется, решил евангелизм будет не лишним. Качество кода может и не шик, но хоть что-то 🙂
Собственно, все нижеследующее порождено в связи с отсутствием чего-либо внятного по поводу Clarion Template Language от классиков жанра, поэтому пишу я, а классики, понадеемся, исправят то, что получится :).
Как сказано ранее- шаблон начинает писаться в тот момент, когда понимаешь что «данный кусок уже писал», таким моментом для меня стал код «Раскраска листбокса», по сути это изподкнопочное окошко, с 4 кнопками настройки цвета столбца (бэкграунд, шрифт и то же для селектированного-бэк и шрифт). Определив глобальную цель, начнем с поиска литературы: в комплекте с c7 идет неплохой хелп (на английском естественно), на вторые сутки перевода которого, ощущение дежавю стало просто осязаемым- все тоже самое, но на русском есть на фтп форума в древнем таком файле авторства Alexandre Katalov. С тех пор ничего нового (в документации) не добавили( на счет самого языка шаблонов- не знаю, если кому есть что сказать — отпишите). Так что Alexandre Katalov это must read.
Далее осилив 10 страниц воды, в доке будет таблицасписок с мясом – классификация всех возможных шаблонов. Для моей задачи прекрасно подходил тип шаблона «Control», если интуитивно, то это все те шняшки, которые можно кидать на форму в режиме WindowFormatter. Все доступные шаблоны такого типа видны из меню PopulateControl Template…
Так как опыт был первый, я не решился браться полностью за разработку шаблона с ноля и полез в реестр шаблонов(Registry Template) с целью превратить HelpButton в конфетку, полностью поменяв ей функционал. Registry Template по сути есть сборник активированных шаблонов, хранящихся в папке %clariontemplate. Хорошо, что в тот раз кнопку Help Button я не нашел. Зато дочитал хелп, в нем говорилось, что править шаблон можно как в ручную, используя любимый Notepad (лежат в %clariontemplate файлы *.tpw), так и из реестра шаблонов(Registry Template) (в IDE меню setuptemplate registry):
И тут вот крайне рекомендуется сделать бэкап папки шаблонов, прежде чем начинать там гениально править 🙂 Хоть среда и делает бэкапы изменений шаблонов(кстати туда же-в %clariontemplate), но только последних.
Еще немного лирики – да, сейчас то я понимаю, что надо было создать шаблон с ноля, это очень просто, всего пара строчек о том, что это шаблон и какого он типа, и потом нажать вон ту кнопку «Register», но тогда я этого не знал, а сказка пишется для совсем новичков и будет полезнее соблюсти хронологию получения дао.
Итак, я полез править кнопку Help, оказалось она лежит в ClassABC->ControlTemplates. Я выбрал ее и нажал «Edit Definition», показалось окно редактора, даже прокрученное до нужного места. Путем нехитрой операции «Ctrl+C ctrl+v», вместо исправления кнопки Help, был-таки получен новый контрол, код которого я частями размещу ниже и постараюсь прокомментировать каждый кусок. Да, еще забыл сказать, могу ошибаться, но в какой-то момент мне запомнилось, что не всё тривиально с форматированием кода в шаблоне, если что-то не работает, хотя вроде должно, возможно перемудрили с пробелами, где не надо.
#!---------------------------------------------------------------- #CONTROL(ColorButton,'Colore the Browse'),WINDOW,multi
Все statement(по-русски это «операнды»?) clarion template language начинаются со знака #.
#! это комментарий, виден только разработчику шаблона. #control это указание, что все нижеследующее это шаблон типа «Control», в скобках его имя, а в кавычках подсказка-описание, слово multi, это указание на то, что таких контролов на одной форме можно наложить много. Вот у кнопки Cancel или Close такого нет, они будучи добавлены на окно, больше не появляются в списке доступных шаблонов.
CONTROLS BUTTON('~'),USE(?Coloriri),AT(,,8,8) END #!
Здесь сказано, что именно будет размещено на форме, когда программер укажет мышью место для размещения моего контрола. В данном случае это одна миллиметрическая кнопка, а например у FuzzyMatcher’а тут целый набор-пара кнопок, группа и ентри.
В принципе уже можно закрыть Реестр Шаблонов и попытаться поглядеть на кнопку из какого–либо проекта:
Кнопка уже умеет делать ничего, но для нее в оконной процедуре сформируется куча embed’ов.
Но вернемся к шаблону- напишем далее:
#ATSTART #DECLARE(%myColorControl) #FOR(%Control),WHERE(%ControlInstance = %ActiveTemplateInstance) #SET(%myColorControl,%Control) #ENDFOR #ENDAT #!
%Control это такая предопределенная обозначает все контролы окна, %ControlInstance зависит от %Control, значение которого внутри цикла на одном значении. Где-то здесь было бы неплохо вообще рассказать о структуре app файла- в нем хранятся начальные состояния шаблонов и их настройки. У каждого добавленного в продцедуру шаблона есть , все номера лежат в %activeTemplateInstance, для нас эта мульти-переменная видимо пофиксирована на номере добавленного экземпляра шаблона (хоть про это нигде и не написано).
Выше я сказал: . A! Переменные в шаблонах зовут Symbol’ами.
#PROMPT('Choose list',control),%ColoririList,default(%ColoririList)
Спросить у программиста, а какой брауз лист мы собираемся раскрашивать? %ColoririList тоже переменная, только ее не обязательно Declare. Она сама декларится, от того, что указана как промпт. это конечно громко сказано, на самом деле программисту надо самому лезть в Properties добавленного контрола, и устанавливать значение в выпадающем списке. Вид промпта задается с помощью второго параметра, тут это , поэтому он и выглядит как выпадающий список с контролами
#PREPARE #FIX(%Control,%ColoririList) #ENDPREPARE #VALIDATE(%ControlType = 'LIST','Must select a list box') #!
Перед валидатом выполнится блок Prepare, он может состоять только из операторов ClarionTemplateLanguage, и используется в основном для всевозможных инициализаций, предустановок и тп. %Control можно на 1 контроле, не только с помощью цикла, но и #Fix’a. Тогда станут доступны всякие пряники, типа %ControlType именно этого контрола. %ControlType это тип контрола, список типов есть в хелпе. #Validate это проверка того, что ввел программист, если программист неправ, ему покажут сообщение. С русским у сообщений, помнится, не дружба.
#! #AT(%datasection),PRIORITY(5030) #for (%control),where(%ControlTemplate=%ActiveTemplate) #if (%myColorControl=%control) QCColor QUEUE,PRE() #!Очередь для колорирования CNam STRING(20) #! FGC LONG #! BGC LONG #! SFC LONG #! SBC LONG #! END #! WindowColor WINDOW('Колорирование'),AT(,,223,153),FONT('MS Sans Serif',8,,FONT:regular),GRAY LIST,AT(5,6,89,143),USE(?ListColor),FORMAT('90L(2)|M*~Колонка~#1#'),FROM(QCColor) STRING('Цвет колонки:'),AT(101,6),USE(?StringFC) BUTTON('ФОН'),AT(154,6,27,10),USE(?ButtonBGC) BUTTON('&OK'),AT(140,135,35,14),USE(?OkButton),LEFT,DEFAULT BUTTON('&Cancel'),AT(182,135,36,14),USE(?CancelButton),LEFT STRING('Под курсором:'),AT(101,18),USE(?StringSC) BUTTON('ШРИФТ'),AT(184,18,33,10),USE(?ButtonSFC) BUTTON('ШРИФТ'),AT(184,6,33,10),USE(?ButtonFGC) BUTTON('ФОН'),AT(154,18,27,10),USE(?ButtonSBC) END #endIF #break #endfor #ENDAT #!
Тут я пытаюсь в секцию данных(#AT(%DataSection)) оконной процедуры вставить код описывающий нужные мне переменные. Для такой операции вообще-то существует блок LocalData, но у меня тут рукописное окошко, его блок LocalData почему-то не съел(добавляет пробел перед именем окна). Цикл в этом блоке делает важную работу- так как шаблон объявлен в качестве multi, то каждый экземпляр его будет пытаться завести одни и теже переменные, а оно совсем не надо. Цикл бежит по всем контролам того же типа(видите там сравнение не по номеру instance, а по имени %ActiveTemplate), и разрешает вставить код только если текущий контрол-Первый своего типа(#if)- там #break, он рвет цикл после Первого прохода.
#! #AT(%ControlEventHandling,%myColorControl,'Accepted'),PRIORITY(5000) free(qCColor) I#=0 loop I#=I#+1 #! Листозависимый код if %ColoririList{PropList:exists,I#}=1 else break. QCColor:Cnam=%ColoririList{PropList:header,I#} QCColor:BGC =%ColoririList{Proplist:BackColor,I#} QCColor:FGC =%ColoririList{Proplist:TextColor,I#} QCColor:SBC =%ColoririList{Proplist:BackSelected,I#} QCColor:SFC =%ColoririList{Proplist:TextSelected,I#} add(QCColor) end Open(windowColor) display(?listColor) ACCEPT #! ОБРАБОТКА ОКОШКА CASE FIELD() of ?ButtonBGC #!БЭКГРАУНД CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:BGC) END put(QCCOLOR) END of ?ButtonFGC #!ШРИФТ CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:FGC) END put(QCCOLOR) END of ?ButtonSBC #!БЭКГРАУНД SELECTED CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:SBC) END put(QCCOLOR) END of ?ButtonSFC #!FONT SELECTED CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:SFC) END put(QCCOLOR) END of ?OkButton CASE EVENT() OF EVENT:Accepted close(WindowColor) END !case event of ?cancelButton case event() of EvenT:accepted close(WindowColor) end! Accepted cancelButton end !case field end ! ACCEPT loop i#=1 to records(QCCOlor) get(QCColor,I#) #! Листозависимый код %ColoririList{PropList:BackColor,I#}=QccOlor:BGC %ColoririList{PropList:TextColor,I#}=QCColor:FGC %ColoririList{PropList:BackSelected,I#}=QccOlor:SBC %ColoririList{PropList:TextSelected,I#}=QCColor:SFC end #ENDAT
Вставка #AT(%ControlEventHandling,%myColorControl,’Accepted’),PRIORITY(5000) вобщем-то ничего нового не скажет(вместо %coloririList вставится имя Listbox’а указанного программистом и всё), разве что укажу способ поиска имен для возможных вставок-бродить по хелпу и файлам TPW, имена для вставок указаны после слова #EMBED.
Послесловие: в основном шаблон удалось написать именно потому, что сам код уже был готов и (после четвертой вставки, если по правде) со мной случился . Также немаловажным я считаю факт, что несколько ранее пришлось пользоваться чужими шаблонами и общая техника их регистрации, подключения и тд уже была. Но самое главное, при написании этого, удалось сломать в себе стереотип, что шаблоны пишут люди, которые хорошо знают clarion, как оказалось — люди их пишут просто по тому, что перед ними стоит такая задача.
Итого в abupdate.tpw добавился следующий код
#!---------------------------------------------------------------- #CONTROL(ColorButton,'Colore the Browse'),WINDOW,multi CONTROLS BUTTON('~'),USE(?Coloriri),AT(,,8,5) END #! #ATSTART #DECLARE(%myColorControl) #DECLARE(%scnd,long) #UNFIX(%Control) #FOR(%Control),WHERE(%ControlInstance = %ActiveTemplateInstance) #SET(%myColorControl,%Control) #ENDFOR #ENDAT #! #PROMPT('Choose list',control),%ColoririList,default(%ColoririList) #PREPARE #FIX(%Control,%ColoririList) #ENDPREPARE #VALIDATE(%ControlType = 'LIST','Must select a list box') #! #! #AT(%datasection),PRIORITY(5030) #for (%control),where(%ControlTemplate=%ActiveTemplate) #if (%myColorControl=%control) QCColor QUEUE,PRE() #!Очередь для колорирования CNam STRING(20) #! FGC LONG #! BGC LONG #! SFC LONG #! SBC LONG #! END #! WindowColor WINDOW('Колорирование'),AT(,,223,153),FONT('MS Sans Serif',8,,FONT:regular),GRAY LIST,AT(5,6,89,143),USE(?ListColor),FORMAT('90L(2)|M*~Колонка~#1#'),FROM(QCColor) STRING('Цвет колонки:'),AT(101,6),USE(?StringFC) BUTTON('ФОН'),AT(154,6,27,10),USE(?ButtonBGC) BUTTON('&OK'),AT(140,135,35,14),USE(?OkButton),LEFT,DEFAULT BUTTON('&Cancel'),AT(182,135,36,14),USE(?CancelButton),LEFT STRING('Под курсором:'),AT(101,18),USE(?StringSC) BUTTON('ШРИФТ'),AT(184,18,33,10),USE(?ButtonSFC) BUTTON('ШРИФТ'),AT(184,6,33,10),USE(?ButtonFGC) BUTTON('ФОН'),AT(154,18,27,10),USE(?ButtonSBC) END #endIF #break #endfor #ENDAT #! #! #AT(%ControlEventHandling,%myColorControl,'Accepted'),PRIORITY(5000) free(qCColor) I#=0 loop I#=I#+1 #! Листозависимый код if %ColoririList{PropList:exists,I#}=1 else break. QCColor:Cnam=%ColoririList{PropList:header,I#} QCColor:BGC =%ColoririList{Proplist:BackColor,I#} QCColor:FGC =%ColoririList{Proplist:TextColor,I#} QCColor:SBC =%ColoririList{Proplist:BackSelected,I#} QCColor:SFC =%ColoririList{Proplist:TextSelected,I#} add(QCColor) end Open(windowColor) display(?listColor) ACCEPT #! ОБРАБОТКА ОКОШКА CASE FIELD() of ?ButtonBGC #!БЭКГРАУНД CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:BGC) END put(QCCOLOR) END of ?ButtonFGC #!ШРИФТ CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:FGC) END put(QCCOLOR) END of ?ButtonSBC #!БЭКГРАУНД SELECTED CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:SBC) END put(QCCOLOR) END of ?ButtonSFC #!FONT SELECTED CASE EVENT() OF EVENT:ACCEPTED get(qCColor,choose(Choice(?ListColor)>0,Choice(?ListColor),1)) IF NOT COLORDIALOG('Choose Box Color',QCColor:SFC) END put(QCCOLOR) END of ?OkButton CASE EVENT() OF EVENT:Accepted close(WindowColor) END !case event of ?cancelButton case event() of EvenT:accepted close(WindowColor) end! Accepted cancelButton end !case field end ! ACCEPT loop i#=1 to records(QCCOlor) get(QCColor,I#) #! Листозависимый код %ColoririList{PropList:BackColor,I#}=QccOlor:BGC %ColoririList{PropList:TextColor,I#}=QCColor:FGC %ColoririList{PropList:BackSelected,I#}=QccOlor:SBC %ColoririList{PropList:TextSelected,I#}=QCColor:SFC end #ENDAT #!----------------------------------------------------------------