RDM/2 The Russian Electronic Developer Magazine  
RDM/2 Русский электронный журнал разработчика  
ДомойОт редактораПишите намОбратная связьRU/2

Похвальное слово EPM

О, сколько нам открытий чудных
Готовит просвещенья дух...
А.С.Пушкин


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


Содержание

Введение

Факт то, что существует ШКОЛА программистов IBM. Это явно проявляется в определенной культуре программирования, наличествующей в продуктах IBM, независимо от платформы (аппаратной или программной).

Также фактом является то, что они это тщательно скрывают :-(. Иначе трудно объяснить наличие в системе (имеется в виду OS/2 Warp, как основной среды разработки автора) средств, позволяющих создать достаточно универсальную и комфортную среду разработки, и отсутствие документации, содержащей описание того, как это сделать. Существующие же документы досточно убоги и запутаны :-(. Попробуйте, хотя бы, разобраться в справке EPM (или вспомнить, как вы это делали в свое время).

А именно на базе EPM (Enhanced Editor, Расширенный Редактор) и можно создать эту среду! Присутствует ощущение того, что IBM предполагала, что EPM будет основным редактором разработчика программного обеспечения, разработала для этого достойный редактор, привязала его к сопутствующим средствам, но при этом не сделала практически ни одного телодвижения, чтобы объяснить как же, в конце концов, им эффективно пользоваться!

Между тем, EPM имеет не только большое количество встроенных функций, действительно необходимых и облегчающих жизнь разработчику, а также перенастраиваемые интерфейс, меню, панель инструментов и горячие клавиши, но и возможность программирования дополнительных функций (макросов) на языках E и REXX (что особенно приятно!).

Поэтому автор решил попытаться обобщить свои знания и приемы работы с EPM и более-менее связно их изложить.

>>> Назад к оглавлению.

Система помощи

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

Разработчики EPM встроили в него чрезвычайно гибкую настраиваемую систему помощи, которая работает следующий образом: Если индексный файл не найден или в нем отсутствует ссылка на выбранную лексему, то в строке состояния отображается соответствующее сообщение об ошибке.

По умолчанию, в качестве индексного используется файл EPMKWHLP.NDX. Переменная среды HELPNDX позволяет переопределить индексные файлы. Она может содержать имя или список имен файлов, которые будут использоваться в качестве индексных; имена файлов в списке разделяются знаками (+), например:
   SET HELPNDX=DDE4.NDX+EPMKWHLP.NDX

Замечание.
Автор не располагает документацией с описанием структуры индексных файлов. Поэтому все предположения относительно этой структуры основаны на догадках (впрочем, весьма прозрачных), сделаных на основе анализа доступных индексных файлов.

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

   ОПЕРАТОР: параметры
Известны (замечены в доступных индексных файлах :-)), по крайней мере, два оператора: За управляющими строками следуют индексные следующего формата:
   (шаблон поиска, шаблон командной строки)
где
шаблон поиска
Шаблон лексемы, по которой производится поиск (регистрозависимый). Шаблон может содержать символ-заменитель, задаваемый оператором EXTENSIONS. Символ-заменитель означает, что вместо него может быть подставлено произвольное количество символов.
шаблон командной строки
Комадная строка, содержащая имя и параметры запуска приложения, выполняющего роль программы просмотра помощи (обычно это команда view). При запуске этого приложения вместо символа тильда (~) в командную строку подставляется лексема, удовлетворящая шаблону поиска.

Пример содержимого индексного файла:
   EXTENSIONS: *
   DESCRIPTION: Custom Index for Toolkit
   (fopen, view clr ~)
   (Win*, view pmref ~)
   ...
Первой индексной строке удовлетворяет лексема fopen, а второй - любая лексема, начинающаяся на Win: WinCreateWindow, WinQueryWindow и т.д. В качестве программы просмотра помощи используется программа view, которая в виде параметров получает имя файла помощи и лексему. Например, если пользователь вызвал подсказку для лексемы WinCreateWindow, то программа просмотра помощи будет запущена следующей командной строкой:
   view pmref WinCreateWindow
После старта view проверяет существует ли уже активное приложение view, просматривающее тот же файл помощи (в данном случае, pmref). Если оно обнаружено, то view просто переключает существующее приложение на просмотр нового раздела. В противном случае продолжает работу, показывая заказанный раздел файла помощи (в данном случае, WinCreateWindow, определенный в одном из тегов :h1.-:h6.).

В качестве программы просмотра помощи можно использовать любое приложение OS/2 - не только PM, но и VIO, а также DOS или WIN-OS2 приложение (в окне или в полный экран). Если приложение не является PM-ным, то запускать его надо командой start из оболочки командной строки.
Например, для просмотра файлов помощи в формате Win из NW SDK можно использовать winhelp:
   (NW*, cmd.exe /c start /c /f /win winhelp.exe c:\nwsdk\winhelp\c_apiref.hlp)
А для просмотра документов в формате HTML вполне подходит links:
   (MENUITEM, cmd.exe /c start /c /f /win links.exe -no-connect file://C:/HTML/rc.html#~)
Однако, немалым преимуществом view является то, что он позволяет переключить ранее запущенное приложение view на просмотр другого раздела помощи вместо запуска нового процесса. Кроме того, использование опции clear в тегах :h1.-:h6. файла помощи позволяет избежать излишнего размножения окон уже внутри самого приложения view.

Таким образом, для создания собственной системы подсказок достаточно просто подготовить необходимые индексные файлы и разместить их в одном из вышеуказанных каталогов.

Поскольку в практической работе почти всегда приходится использовать больше одной системы программирования (по крайней мере, REXX и еще один язык программирования - C/С++, Pascal и т.д.), то желательно получать помощь именно в контексте используемого языка, т.к. операторы и функции в этих языках имеют, как правило, одинаковые названия (при всех прочих различиях). Поэтому крайне неудобно объединять в один индексный файл ссылки для разных языков.

Помочь в решении этой проблемы может, например, макрос gethelp.erx (именно такое расширение использует EPM, чтобы не путать с командными файлами самого REXX):
   /* Get help                       */
   /* Copyleft VicTor Smirnoff, 2001 */

   'extract /filename'
   fname = filespec( "name", filename.1 )
   d = lastpos( '.', fname )

   if d > 0 then
   do
      fext = translate( substr( fname, d+1 ) )

      select
      when wordpos( fext, "RC DLG" ) > 0  then
         xHelp = 'EPMKWRC.NDX'
      when wordpos( fext, "CMD ERX" ) > 0  then
         xHelp = 'EPMKWRX.NDX'
      otherwise
         xHelp = ''
      end

      rc = setlocal()
      xOld = Value( 'HELPNDX', , 'OS2ENVIRONMENT' )
      if xHelp \= '' then
      do
         xNew = xHelp||'+EPMKWHLP.NDX'
         if xOld \= '' then xNew = xHelp||'+'||xOld
         rc = Value( 'HELPNDX', xNew, 'OS2ENVIRONMENT' )
      end
      'dokey c+H'
      rc = endlocal()

   end
Макрос проверяет расширение редактируемого файла, в соответствии с ним выбирает имя индексного файла, которое добавляет в начало последовательности просмотра, задаваемое переменной среды HELPNDX. Затем вызывает систему помощи (команда dokey, к сожалению, недокументированная, но о ней позже) и восстанавливает предыдущее значение переменной среды HELPNDX.

Этот макрос можно выполнить из диалога "Команда" с помощью команды rx, назначить его вызов на кнопку полосы инструментов, пункт меню или одну из горячих клавиш (например, на ту же самую комбинацию Ctrl-H).

>>> Назад к оглавлению.

Подсветка ключевых слов

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

Без дополнительных ухищрений в EPM доступен только один способ - создать кнопку на полосе инструментов (для этого надо щелкнуть по полосе инструментов кнопкой 2 мыши и выбрать пункт меню "Создать элемент...") и назначить ей на выполнение функцию a_Togl_Hilit. Теперь эта кнопка будет включать/отключать подсветку ключевых слов.

Файлы со схемами подсветки ключевых слов находятся в каталоге, указанном в переменной среды EPMPATH (хотя есть основания полагать, что последовательность поиска этих файлов аналогична последовательности поиска индексных файлов, т.е. сначала в текущем каталоге, потом в каталогах, указанных в переменных среды EPMPATH и DPATH, затем в каталоге, из которого загружен EPM, и, наконец, в каталогах, указанных в переменной среды PATH). Эти файлы имеют имя EPMKWDS и те же самые расширения, что и файлы, для которых осуществляется подсветка ключевых слов. Например, для файлов с расширением *.C используется файл EPMKWDS.C (кстати, именно в нем находится описание правил составления схем подсветки).

Строго говоря, функция a_Togl_Hilit применяет некоторые схемы подсветки к группам родственных файлов, например, EPMKWDS.C - для подсветки ключевых слов в файлах исходных текстов C/C++ (т.е. *C, *.H, *.CPP, *.HPP и других), а EPMKWDS.CMD - для *.CMD, *.ERX, *.VRX и прочих файлов REXX.

К сожалению, функцию a_Togl_Hilit нельзя назначить на выполнение пункту меню или горячей клавише. Ее также нельзя выполнить из диалога "Команда" или вызвать из макроса REXX. И, конечно, в ней нельзя переопределить группы родственных файлов, использующих одну схему подсветки.

Здесь может помочь макрос hilite.erx:
   /* Hilite keywords                */
   /* Copyleft VicTor Smirnoff, 2001 */

   'extract /filename'
   fname = filespec( "name", filename.1 )
   d = lastpos( '.', fname )

   if d > 0 then
   do
      fext = translate( substr( fname, d+1 ) )

      select
      when wordpos( fext, "C H CPP HPP" ) > 0 then
         fext = 'C'
      when wordpos( fext, "RC DLG" ) > 0  then
         fext = 'RC'
      when wordpos( fext, "BAT CMD ERX VRX" ) > 0  then
         fext = 'CMD'
      when wordpos( fext, "HTM HTML" ) > 0  then
         fext = 'HTM'
      otherwise
         nop
      end

      'extract /userstring'
      parse upper var userstring.1 xA'HILITE='xHi';'xB

      yHi = 1
      if xHi = 1 then yHi = 0

      call etksetfilefield 'userstring', xA||'HILITE='||yHi||';'||xB

      'toggle_parse '||yHi||' epmkwds.'||fext
   end
Макрос проверяет расширение редактируемого файла, в соответствии с ним и группами родственных файлов выбирает расширение для файла схемы подсветки. Затем проверяет состояние собственного флага HILITE, хранимого в поле userstring (приходится делать так, потому что добраться до флага, отвечающего за подсветку текста в окне, из REXX-а не удается), в сответствии с ним переключает режим подсветки (команда toggle_parse, тоже недокументированная и тоже о ней позже) и сохраняет новое состояние флага HILITE.

>>> Назад к оглавлению.

Макросы REXX для работы в кольце

Во время работы с текстами программ не очень удобно иметь на Рабочем Столе кучу раскрытых окон, в которых находятся редактируемые файлы исходных текстов. Гораздо удобнее воспользоваться возможностью редактирования файлов в кольце, предоставляемую EPM (меню "Режимы -> Привычный выбор -> Кольцо разрешено"). Переключаться между файлами, загруженными в кольцо, можно с помощью клавиш F11, F12 или диалога "Список файлов кольца", вызываемого комбинацией клавиш Ctrl-G.

Для загрузки всех файлов исходных текстов проекта в кольцо можно использовать макрос addall.erx:
   /* Add all source files           */
   /* Copyleft VicTor Smirnoff, 2001 */

   currentID = rxGetFileId()
   'edit *.c,*.cpp,*.h,*.hpp,*.rc,*.dlg'

   newID = rxGetFileId()
   xID = 0
   do while xID \= newID
      call hilite.erx
      call etkprocesseditkey "NEXT_FILE"    /* dokey F12 */
      xID = rxGetFileId()
   end

   xID = rxGetFileId()
   do while xID \= currentID
      call etkprocesseditkey "NEXT_FILE"    /* dokey F12 */
      xID = rxGetFileId()
   end
   exit

   rxGetFileId: procedure
      call etkprocesseditkey "UNMARK"
      call etkprocesseditkey "MARK_LINE"
      'extract /getmark'
      call etkprocesseditkey "UNMARK"
      return getmark.5
Макрос получает идентификатор текущего файла в кольце (это не хэндл; тут надо отметить, что каждый файл в кольце имеет свой идентификатор, правда, путь к его получению весьма нетривиален) и загружает в кольцо все существующие файлы исходных текстов из текущего каталога. Затем макрос прокручивает файлы в кольце, включает подсветку ключевых слов для каждого и, наконец, делает текущим первоначальный файл.

Очень полезен также макрос saveall.erx, позволящий сохранить все измененные файлы:
   /* Save all modified files        */
   /* Copyleft VicTor Smirnoff, 2001 */

   currentID = rxGetFileId()
   xID = 0
   do while xID \= currentID
      'extract /modify'
      if modify.1 \= 0 then 'save'
      call etkprocesseditkey "NEXT_FILE"    /* dokey F12 */
      xID = rxGetFileId()
   end
   exit

   rxGetFileId: procedure
      call etkprocesseditkey "UNMARK"
      call etkprocesseditkey "MARK_LINE"
      'extract /getmark'
      call etkprocesseditkey "UNMARK"
      return getmark.5
Макрос получает идентификатор текущего файла, прокручивает файлы в кольце и сохраняет каждый модифицированный файл (за исключением новых, без имени).

Кто как, а автор предпочитает работать с исходными текстами, когда они отображаются в окне редактора шрифтами постоянной ширины. Лучше всего, по мнению автора, подходит растровый шрифт System VIO размера 16x8, поскольку в нем визуально очень хорошо различаются прописная буква "I" (ай), строчная буква "l" (эл) и цифра "1" (один).

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

Выручить может макрос togglefont.erx:
/* Toggle mono/proportional fonts */
/* Copyleft VicTor Smirnoff, 2001 */

   'extract /font'
   if font.1 = 1 then
      call etksetfilefield 'font', 2
   else
      call etksetfilefield 'font', 1
Не менее полезен макрос searchprocedure.erx (он слишком велик, чтобы приводить в тексте статьи, см. архив myepm.zip), выполняющий поиск исходного текста функции с файлах кольца (пока только для C/C++).

Стандартные решения, предлагаемые EPM (теги, команды tagsfile и maketags, поиск в тегах по комбинациям клавиш Shift-F6 и Shift-F7), не очень удобны для работы в кольце, поскольку добавляют в кольцо лишнии копии файлов во время выполнения поиска.

Макрос функционально подобен стандартной функции поиска текущей процедуры - необходимо установить курсор на имя функции и выполнить макрос. Макрос сохранит текущую позицию курсора и выполнит поиск исходного текста этой функции во всех файлах кольца. Если он будет найден, то текущим становится файл, содержащий этот исходный текст, а курсор позиционируется на найденную функцию. Позже можно вернуться к прерванной работе, восстановив позицию курсора (комбинация клавиш Ctrl-ArrowUp).

>>> Назад к оглавлению.

Документирование исходных текстов

Всегда легче и приятнее работать с красиво оформленными исходными текстами программ. Особенно, если тексты снабжены необходимыми и достаточными комментариями :-).

Здесь весьма полезными могут быть макросы insertcomment.erx, insertfunction.erx и insertheader.erx.

Макрос insertcomment.erx вставляет заготовку блока комметария перед строкой, на которою установлен курсор:
   /* Insert comment                 */
   /* Copyleft VicTor Smirnoff, 2001 */

   'extract /filename/last'
   fname = filespec( "name", filename.1 )
   d = lastpos( '.', fname )

   if d > 0 then
   do
      fext = translate( substr( fname, d+1 ) )

      select
      when wordpos( fext, "C H CPP HPP RC DLG CMD ERX VRX" ) > 0 then
      do
         call etksetfilefield 'col', 1
         call etkinserttext substr('/',1,75,'*')||'/'
         call etkinserttext substr('/*',1,74,' ')||'*/'
         call etkinserttext substr('/',1,75,'*')||'/'
      end
      otherwise
         nop
      end

   end
Макрос insertfunction.erx оформляет заготовками блоков комментариев исходный текст функции. Для этого надо установить курсор на имя функции и выполнить макрос. Например, исходный текст функции MyCoolFunction() будет снабжен следующими заготовками комментариев:
   /**************************************************************************/
   /* MyCoolFunction()                                                       */
   /*========================================================================*/
   /* Описание:                                                              */
   /*                                                                        */
   /* Синтаксис:                                                             */
   /*    rc = MyCoolFunction(idWho,cName,idWhere,cStreet);                   */
   /*                                                                        */
   /* Параметры:                                                             */
   /*    idWho                                                               */
   /*    cName                                                               */
   /*    idWhere                                                             */
   /*    cStreet                                                             */
   /*                                                                        */
   /* Возврат:                                                               */
   /*    rc                                                                  */
   /*                                                                        */
   /*------------------------------------------------------------------------*/
   INT MyCoolFunction( INT idWho, CHAR *cName, INT idWhere, CHAR *cStreet )
   {
      . . .
   }
   /*------------------------------------------------------------------------*/
   /* Конец функции MyCoolFunction()                                         */
   /**************************************************************************/
Аналогично, макрос insertheader.erx оформляет заготовками блоков комментариев файл исходных текстов. Например, файл MyCoolRoutine.c будет снабжен следующими заготовками комментариев:
   /**************************************************************************/
   /* MyCoolRoutine.c - исходные тексты программы                            */
   /*                                                                        */
   /*========================================================================*/
   /* (c) VicTor Smirnoff, 2001                         sva@water.karelia.ru */
   /*------------------------------------------------------------------------*/
   #define INCL_PM
   #include <os2.h>

      . . .


   /*------------------------------------------------------------------------*/
   /* Конец файла MyCoolRoutine.c                                            */
   /**************************************************************************/
А макросы formattext.erx и formatselect.erx позволяют отформатировать текстовые файлы, без которых не обходится ни одна разработка.

>>> Назад к оглавлению.

Полоса инструментов

Встроенная полоса инструментов EPM мало подходит для разработчика программного обеспечения и совершенно неприглядна по дизайну. Но, видимо, поскупившись на художников, IBM не зря кормит программистов :-), которые предоставили все средства для приведения полосы инструментов в достойный вид (что, в прочем, каждый понимает по-своему).

Например, полоса инструментов выглядит значительно лучше, если содержит только небольшие рисунки (не больше, чем 16x16), как на рисунке слева.

Трудность состоит в том, что эти рисунки должны быть достаточно информативными и "интуитивно" понятными, чтобы не требовались подписи. И чтобы их придумать и нарисовать, требуется немалое умение и талант. А тут уже без дизайнера не обойтись. Хотя каждый может попробовать сам :-).

Теперь о том, что как делать.

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

Для того, чтобы создать, удалить или изменить кнопку, достаточно щелкнуть на полосе инструментов (или любой кнопке полосы) кнопкой 2 мыши и выбрать соответствующий пункт меню. Если кнопка полосы должна исполнять макрос, то на странице "Действие" соответствующего диалога поле "Команда" должно содержать символ *, а поле "Параметры" команду RX <имя макроса>. Страница "Значок" позволяет загрузить и использовать в качестве значка любой файл в формате *.BMP.

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

Однако, это еще не означает, что в следующий раз EPM стартует с новой полосой инструментов. Необходимо еще на странице "Полосы" диалога "Параметры" выбрать новую полосу, сделать ее текущей и сохранить параметры.

>>> Назад к оглавлению.

Настройка при запуске

Это еще одна замечательная особенность этого редактора. Дело в том, что EPM имеет возможность выполнить макрос profile.erx, содержащий пользовательские команды настройки (профиль), во время старта. Поиск макросов выполняет EPM (а не REXX) - сначала в текущем каталоге, потом в каталогах, указанных в переменных среды EPMPATH и PATH. Для того, чтобы активизировать эту возможность, на страничке "Разное" диалога "Параметры" надо включить переключатель "Профиль REXX".

Например, профиль может выполнить следующие настройки:
   /* Profile REXX                   */
   /* Copyleft VicTor Smirnoff, 2001 */

   AF_CHAR        =   1   /* константы стиля клавиш */
   AF_VIRTUALKEY  =   2
   AF_SCANCODE    =   4
   AF_SHIFT       =   8
   AF_CONTROL     =  16
   AF_ALT         =  32
   AF_LONEKEY     =  64

   VK_F1        = 32
   VK_ALT       = 11

   MIS_SEPARATOR = 4

   /* Настройка опций */
   /* При поиске, по умолчанию, не учитывается регистр */
   'universal default_search_options C'
   /* При загрузке файла преобразовать табуляции в пробелы */
   'universal default_edit_options /nt'
   /* Удалить пробелы на концах строк при сохранении файла */
   'universal default_save_options /s'

   /* Настройка горячих клавиш */
   /* ASCII 104 = 'h' */
   /* ASCII 122 = 'z' */
   /* Ctrl-h получить справку */
   /* Ctrl-z заключить бокс в рамку комментариев C */
   /* Alt-z  убрать рамку вокруг бокса */
   'buildaccel *' (AF_CHAR + AF_CONTROL)  104 9302 'rx gethelp'
   'buildaccel *' (AF_CHAR + AF_CONTROL)  122 9301 'box c'
   'buildaccel *' (AF_CHAR + AF_ALT)      122 9300 'box e'
   /* Запрещает использование клавиши Alt для перехода на полосу меню.*/
   'buildaccel *' (AF_VIRTUALKEY+AF_LONEKEY) VK_ALT 9399 'sayerror F10=полоса меню'
   'activateaccel'  /* Нет аргумента => используется текущее имя таблицы. */

   /* Настройка меню */
   'buildsubmenu default 1990 Исходники 0 0'
   'buildmenuitem default 1990 1991 Подсветка 0 0 rx hilite'
   'buildmenuitem default 1990 1992 Добавить_Все 0 0 rx addall'
   'buildmenuitem default 1990 1993 x' MIS_SEPARATOR '0'
   'buildmenuitem default 1990 1994 Вставить_Заголовок_Файла 0 0 rx insertheader'
   'buildmenuitem default 1990 1995 Вставить_Заголовок_Функции 0 0 rx insertfunction'
   'buildmenuitem default 1990 1996 Вставить_Комментарий 0 0 rx insertcomment'
   'showmenu default'

>>> Назад к оглавлению.

Заключение

Конечно, нереально рассмотреть все возможности EPM в одной статье. Можно сказать, что макросы REXX и программирование функций на языке E делают его редактором с неограниченными возможностями (даже в стандартной поставке OS/2 Warp). Удручает только документация...

EPM v5.x входит в состав дистрибутива OS/2 Warp 3.0, а EPM v6.03b - OS/2 Warp v4.0 и v4.5. Автор не работал с EPM версии младше, чем 6.03b, поэтому работоспособность всех макросов проверена только для этой версии.

Кроме того, существует комплект, называемый EPMBBS, содержащий последнюю (в данный момент 6.03b) версию редактора и компилятор E в соответствующем сопровождении (примеры, документация и т.д.). Получить его можно прямо с FTP-сервера IBM.

В макросах gethelp.erx и hilite.erx использованы две недокументированные функции: dokey и toggle_parse. Эти и еще многие другие полезные функции доступные из REXX, находятся в файле EPM.EX, исходный текст которого (EPM.E) тоже включен в комплект EPMBBS.

>>> Назад к оглавлению.

Примеры полосы инструментов, битмапы и тексты макросов REXX и находятся в архиве myepm.zip.

Успехов,

VicTor Smirnoff

---
Интересные ссылки:

---

---
Комментариев к странице: 0 | Добавить комментарий
---
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов
(C) Russian Underground/2