Текущий раздел:

Описания RDS

Руководство программиста

Глава 2. Создание моделей блоков

§2.7. Настройка параметров блока

§2.7.1. Функция настройки блока и открытие модальных окон

# Руководство программиста

## Глава 2. Создание моделей блоков

### §2.7. Настройка параметров блока

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

#### §2.7.1. Функция настройки блока и открытие модальных окон

Описывается реакция модели на функцию настройки блока (RDS_BFM_SETUP), возможность вызова которой встроена в пользовательский интерфейс RDS. С помощью этой реакции в блоки, описанные в §2.6.3 (приемник и передатчик, организующие связь через динамическую переменную), добавлен интерфейс для настройки.

Функция настройки обычно используется для того, чтобы организовать какой-либо диалог с пользователем, например, дать ему возможность задать значения параметров блока. Эта функция может вызываться только в режиме редактирования, поэтому в ней можно открывать модальные окна, не опасаясь зависания потока расчета (проблемы, возникающие при открытии модальных окон в режиме расчета, описаны в §1.8). Для того, чтобы пользователь мог вызвать функцию настройки, необходимо разрешить ее, установив на вкладке «DLL» окна параметров блока флаг «блок имеет функцию настройки» (при этом можно указать название пункта контекстного меню, при выборе которого будет вызываться эта функция, см. рис. 105), и ввести в модель реакцию на событие RDS_BFM_SETUP. Модель, вызванная для реакции на это событие, должна самостоятельно открыть диалоговое окно (средствами Windows или с помощью сервисных функций RDS), и, после его закрытия, вернуть RDS одну из двух констант:

- RDS_BFR_DONE – параметры блока не изменились;
- RDS_BFR_MODIFIED – введены новые параметры блока, схема будет считаться измененной и при попытке выхода из RDS без сохранения схемы пользователю будет выдано предупреждение.

Для открытия диалогового окна функция модели может использовать любые доступные ей библиотечные функции (например, функцию DialogBox в Windows API, функцию-член ShowModal потомков класса TForm в Borland C++ Builder и т.п.). При этом, чтобы избежать проблем, описанных в §1.8, модель должна сообщать RDS об открытии и закрытии диалогового окна при помощи сервисных функций rdsBlockModalWinOpen и rdsBlockModalWinClose. В RDS также предусмотрено несколько сервисных функций, позволяющих открывать диалоговые окна. Эти функции не предоставляют такой свободы в задании внешнего вида окна, как универсальные функции API, зато при их использовании можно не беспокоиться о синхронизации открытия и закрытия окон с RDS.

Если модели требуется запросить у пользователя только один параметр, проще всего использовать для этого сервисную функцию rdsInputString, которая открывает диалоговое окно для ввода строки. Функция принимает следующие параметры (в разных кодировках):

      RDSSTR RDSCALL rdsInputStringA(
        RDSCSTR WinCaption,  // Заголовок окна
        RDSCSTR StrCaption,  // Текст перед полем ввода
        RDSCSTR Default,     // Исходное значение
        int Width);          // Ширина поля ввода
      RDSWSTR RDSCALL rdsInputStringW(
        RDSWCSTR WinCaption, // Заголовок окна
        RDSWCSTR StrCaption, // Текст перед полем ввода
        RDSWCSTR Default,    // Исходное значение
        int Width);          // Ширина поля ввода
      // Функция-псевдоним
      RDSXSTR RDSCALL rdsInputString(
        RDSXCSTR WinCaption, // Заголовок окна
        RDSXCSTR StrCaption, // Текст перед полем ввода
        RDSXCSTR Default,    // Исходное значение
        int Width);          // Ширина поля ввода

WinCaptionA(RDSCSTR),WinCaptionW(RDSWCSTR),WinCaption(RDSXCSTR)
  Заголовок диалогового окна.
StrCaptionA(RDSCSTR),StrCaptionW(RDSWCSTR),StrCaption(RDSXCSTR)
  Текст перед полем ввода (обычно он сообщает пользователю, какой параметр он вводит).
DefaultA(RDSCSTR),DefaultW(RDSWCSTR),Default(RDSXCSTR)
  Исходное значение строки, которое помещается в поле ввода перед редактированием.
Width(int)
  Ширина поля ввода строки в точках экрана.

Функция открывает диалоговое окно с единственным полем ввода и кнопками «OK» и «Отмена». Размер окна автоматически подбирается таким образом, чтобы в него уместилось поле ввода указанного в параметре Width размера и текст, указанный в параметре StrCaption, перед ним. Если пользователь нажмет кнопку «OK», функция вернет указатель на динамически отведенную область памяти, в которую скопирована строка из поля ввода (эту область необходимо будет позже освободить при помощи сервисной функции rdsFree). Если пользователь нажмет кнопку «Отмена», функция вернет константу NULL.

Эту функцию можно использовать в примере с блоками-передатчиками и приемниками для задания имени динамической переменной, связывающей эти блоки между собой. В примере имя этой переменной хранится в комментарии блока, и пользователь может изменить его только в окне параметров этого блока на вкладке «общие». Это не очень удобно, поскольку пользователю не выдается никаких подсказок о том, где именно вводится имя переменной. При помощи функции rdsInputString можно организовать ввод имени переменной в отдельном окне, с заголовком, дающим пользователю понять, что он вводит имя переменной для связи блоков. Диалоговое окно будет открываться в момент реакции модели на вызов функции настройки. Для того, чтобы блок начал реагировать на это событие, необходимо в окне его параметров на вкладке «DLL» установить флаг «блок имеет функцию настройки». Кроме того, на вкладке «общие» можно установить флаг «двойной щелчок в режиме редактирования вызывает функцию настройки» (см. рис. 5), чтобы пользователю не нужно было выбирать пункт «настройка» в контекстном меню каждый раз, как ему понадобится изменить имя переменной. Эти действия следует произвести со всеми блоками-приемниками и блоками-передатчиками (можно воспользоваться функцией групповой установки).

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

Сначала в обе модели нужно добавить описание новой вспомогательной переменной str, которой будет присваиваться указатель, возвращаемый функцией rdsInputString (добавленный текст выделен цветом):

      // …
      // Вспомогательная переменная – указатель на структуру подписки
      RDS_PDYNVARLINK Link;
      // Вспомогательная переменная – структура описания блока
      RDS_BLOCKDESCRIPTION Descr;
      // Вспомогательная переменная – указатель на строку
      char *str;

      switch(CallMode)
        { // Очистка
      // …

Внутрь оператора switch(CallMode) необходимо добавить новый оператор case с константой RDS_BFM_SETUP:

      //…
          // Реакция на функцию настройки
          case RDS_BFM_SETUP:
            // Получение текущего текста комментария блока
            Descr.servSize=sizeof(Descr);
            rdsGetBlockDescription(BlockData->Block,&Descr);
            // Открытие диалогового окна для ввода строки
            str=rdsInputString("Переменная связи", // Заголовок окна
                               "Имя переменной:",  // Заголовок поля
                               Descr.BlockComment, // Исходный текст
                               150);               // Ширина поля
            if(str!=NULL) // Пользователь нажал "OK"
              { // Установить новый комментарий
                rdsSetBlockComment(BlockData->Block,str);
                // Освободить динамическую строку
                rdsFree(str);
                // Информировать RDS об изменениях
                return RDS_BFR_MODIFIED;
              }
            break;
        }
      return RDS_BFR_DONE;
      // …

Рис. 46. Модальное окно ввода строки

При вызове модели с параметром RDS_BFM_SETUP прежде всего необходимо получить текущее значение комментария блока, чтобы предъявить пользователю для редактирования имя переменной, используемой блоком в данный момент. Для этого вызывается функция rdsGetBlockDescription, заполняющая структуру описания блока (она уже использовалась в этих моделях). После ее вызова в поле структуры Descr.BlockComment будет записан указатель на строку комментария блока. Теперь это значение можно передать в функцию rdsInputString, которая откроет диалоговое окно для редактирования строки с заголовком «Переменная связи» (рис. 46). Перед полем ввода будет выведен заголовок «Имя переменной:», а само поле будет содержать текст из комментария блока (или пустую строку, если комментарий был пуст). Если пользователь изменит текст в поле ввода и нажмет кнопку «OK», функция rdsInputString вернет указатель на динамически сформированную строку, скопированную из поля ввода, в противном случае она вернет NULL. Возвращенное функцией значение присваивается вспомогательной переменной str. Если оно не равно NULL (то есть пользователь нажал «OK»), эта строка устанавливается в качестве нового комментария блока при помощи сервисной функции rdsSetBlockComment, после чего занятая ей память освобождается при помощи функции rdsFree. Затем функция модели возвращает константу RDS_BFR_MODIFIED, информируя RDS о том, что параметры блока изменились и требуется предупредить об этом пользователя, если он захочет выйти из программы, не сохранив схему.

Важное замечание. Описанная модель будет правильно работать только в том случае, если файл исходного текста программы записан в кодировке UTF-8. Дело в том, что в функцию rdsInputString передаются строковые константы (строки в двойных кавычках), а по умолчанию они считаются записанными именно в кодировке UTF-8. Если по какой-либо причине текст программы пишется в другой кодировке, можно явно использовать функцию rdsInputStringW, работающую с принятой в программах Windows кодировкой UTF-16. При этом изменение кодировки необходимо будет также учитывать и при чтении комментария блока (следует использовать поле Descr.BlockCommentW), и при его установке (следует вызывать функцию rdsSetBlockCommentW). Функцию rdsFree заменять не нужно, она освобождает память, отведенную в RDS, независимо от формата данных в этой памяти.

С использованием кодировки UTF-16 реакция на функцию настройки будет выглядеть так (изменения выделены цветом):

      //…
          // Реакция на функцию настройки
          case RDS_BFM_SETUP:
            { RDSWSTR str; // Указатель на строку в UTF16
              // Получение текущего текста комментария блока
              Descr.servSize=sizeof(Descr);
              rdsGetBlockDescription(BlockData->Block,&Descr);
              // Открытие диалогового окна для ввода строки
              str=rdsInputStringW(L"Переменная связи", // Заголовок окна
                                  L"Имя переменной:",  // Заголовок поля
                                  Descr.BlockCommentW, // Исходный текст
                                  150);                // Ширина поля
              if(str!=NULL) // Пользователь нажал "OK"
                { // Установить новый комментарий
                  rdsSetBlockCommentW(BlockData->Block,str);
                  // Освободить динамическую строку
                  rdsFree(str);
                  // Информировать RDS об изменениях
                  return RDS_BFR_MODIFIED;
                }
            }
            break;
      // …

Здесь вводится вспомогательный локальный указатель str на строку в кодировке UTF-16 (тип RDSWSTR), перекрывающий видимость «старой» переменной с тем же именем, а для символьных констант в UTF-16 используется префикс «L».