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

Описания RDS

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

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

§2.13. Вызов функций блоков

§2.13.3. Прямой вызов функции всех блоков подсистемы

Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока, иллюстрирующей прямой вызов функции у всех блоков подсистемы. В модель EditControlFrame (блок с прозрачным для щелчков мыши «окном», см. §2.12.3) добавлена возможность одновременного «открытия» и «закрытия» всех таких блоков в подсистеме. Изменения относительно предыдущей версии модели выделены цветом).

      // Прямой вызов функции всех блоков подсистемы
      #include <windows.h>
      #include <RdsDef.h>
      // Подготовка описаний сервисных функций
      #define RDS_SERV_FUNC_BODY GetInterfaceFunctions
      #include <RdsFunc.h>

      //========== Главная функция DLL ==========
      int WINAPI DllMain(HINSTANCE /*hinst*/,
                               unsigned long reason,
                               void* /*lpReserved*/)
      { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL
          { // Получение доступа к функциям RDS
            if(!GetInterfaceFunctions())
              RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS
          }
        return 1;
      }
      //========= Конец главной функции =========

      //=========================================
      // Функция управления состоянием блока
      //=========================================
      // Имя функции
      #define PROGGUIDEEDITCTRLFUNC_SET \
                "ProgrammersGuide.EditCtrlFrame.Set"
      // Структура параметров функции
      typedef struct
      { DWORD servSize; // Размер этой структуры для проверки
        int Command;    // Команда блоку (0, 1 или 2)
      } TProgGuideEditCtrlSetParams;
      //=========================================
      // Идентификатор функции PROGGUIDEEDITCTRLFUNC_SET
      int EditCtrlFuncSet=0;
      //============================================================================
      // Блок с "окном"
      //============================================================================
      extern "C" __declspec(dllexport)
        int RDSCALL EditControlFrame(int CallMode,
                RDS_PBLOCKDATA BlockData,
                LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define x_ext (*((double *)(pStart+2*RDS_VSZ_S)))
      #define x_int (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
      #define out (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D)))
      #define bypass (*((char *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_D)))
        const int fr=20; // Толщина рамки
        // Вспомогательные переменные
        RDS_PMOUSEDATA mouse;
        RDS_PDRAWDATA draw;
        int frz,x1,y1,x2,y2,xi1,yi1,xi2,yi2;

        RDS_PFUNCTIONCALLDATA func; // Описание вызванной функции
        RDS_PMENUFUNCDATA menu;     // Описание выбранного пункта меню
        TProgGuideEditCtrlSetParams callparams; // Параметры функции
        switch(CallMode)
          {
            // Инициализация блока
            case RDS_BFM_INIT:
              if(!EditCtrlFuncSet)
                EditCtrlFuncSet=rdsRegisterFunction(PROGGUIDEEDITCTRLFUNC_SET);
              break;
            // Проверка типов статических переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSDDDL}")?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;

            // Реакция на нажатие кнопки мыши
            case RDS_BFM_MOUSEDOWN:
              mouse=(RDS_PMOUSEDATA)ExtParam;
              // Толщина рамки с учетом масштаба
              frz=fr*mouse->DoubleZoom;
              // В открытом состоянии при попадании курсора внутрь
              // прозрачного окна на щелчок реагировать не нужно
              if(bypass==0 &&
                 mouse->x>mouse->Left+frz &&
                 mouse->y>mouse->Top+frz &&
                 mouse->x<mouse->Left+mouse->Width-frz &&
                 mouse->y<mouse->Top+mouse->Height-frz)
                return RDS_BFR_NOTPROCESSED;
              // Если не левая кнопка - не обрабатываем щелчок
              // и разрешаем вывести контекстное меню, если нужно
              if(mouse->Button!=RDS_MLEFTBUTTON)
                return RDS_BFR_SHOWMENU;
              // Нажата левая кнопка мыши, причем курсор попал
              // в рамку или блок в закрытом состоянии
              bypass=!bypass; // Переключаем состояние
              Ready=1;        // Взводим сигнал готовности
              // Здесь намеренно не поставлен оператор break: необходимо
              // выполнить действия в следующем case (такт расчета)

            // Один такт расчета
            case RDS_BFM_MODEL:
              // В зависимости от состояния, подаем на выход один из входов
              out=bypass?x_ext:x_int;
              break;

            // Рисование внешнего вида блока
            case RDS_BFM_DRAW:
              draw=(RDS_PDRAWDATA)ExtParam;
              // Координаты описывающего прямоугольника блока
              x1=draw->Left;
              x2=draw->Left+draw->Width;
              y1=draw->Top;
              y2=draw->Top+draw->Height;
              // Толщина рамки с учетом масштаба
              frz=fr*draw->DoubleZoom;
              // Координаты окна внутри блока
              xi1=x1+frz;
              xi2=x2-frz;
              yi1=y1+frz;
              yi2=y2-frz;
              if(bypass)
                { // Закрытое состояние
                  int w;
                  char *text;
                  // Рисуем красный прямоугольник с черной рамкой
                  rdsXGSetPenStyle(0,PS_SOLID,1,0,R2_COPYPEN);
                  rdsXGSetBrushStyle(0,RDS_GFS_SOLID,0xff);
                  rdsXGRectangle(x1,y1,x2,y2);
                  // Устанавливаем шрифт выстой в окно внутри блока
                  rdsXGSetFont(0,"Arial",yi2-yi1,0,
                               DEFAULT_CHARSET,0,FALSE,FALSE,FALSE,FALSE);
                  // Преобразуем значение выхода в динамическую строку
                  text=rdsDtoA(out,-1,NULL);
                  // Определяем ширину получившейся строки на экране
                  rdsXGGetTextSize(text,&w,NULL);
                  // Выводим значение выхода туда, где в открытом
                  // состоянии находится прозрачное окно
                  rdsXGTextOut(xi2-w,yi1,text);
                  // Освобождаем динамическую строку
                  rdsFree(text);
                }
              else
                { // Открытое состояние
                  rdsXGSetBrushStyle(0,RDS_GFS_SOLID,0xff00);
                  // Рисуем рамку вокруг прозрачного окна из четырех
                  // зеленых прямоугольников
                  rdsXGFillRect(x1,y1,x2,yi1);
                  rdsXGFillRect(x1,yi2,x2,y2);
                  rdsXGFillRect(x1,yi1,xi1,yi2);
                  rdsXGFillRect(xi2,yi1,x2,yi2);
                  // Обрамляем черными линиями
                  rdsXGSetPenStyle(0,PS_SOLID,1,0,R2_COPYPEN);
                  rdsXGSetBrushStyle(0,RDS_GFS_EMPTY,0);
                  rdsXGRectangle(x1,y1,x2,y2);
                  rdsXGRectangle(xi1,yi1,xi2,yi2);
                }
              break;

            // Проверка возможности выбора блока мышью
            case RDS_BFM_MOUSESELECT:
              mouse=(RDS_PMOUSEDATA)ExtParam;
              frz=fr*mouse->DoubleZoom; // Толщина рамки
              // Проверка попадания в прозрачное окно
              if(bypass==0 &&
                 mouse->x>mouse->Left+frz &&
                 mouse->y>mouse->Top+frz &&
                 mouse->x<mouse->Left+mouse->Width-frz &&
                 mouse->y<mouse->Top+mouse->Height-frz)
                return RDS_BFR_NOTPROCESSED;
              // В окно не попали - функция вернет RDS_BFR_DONE
              break;

            // Открытие контекстного меню блока
            case RDS_BFM_CONTEXTPOPUP:
              // Добавление временных пунктов меню
              // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
              //   в противном случае необходимо использовать версии функций
              //   с суффиксом "W" и символьные константы с префиксом "L"
              rdsAdditionalContextMenuItemEx(bypass?"Открыть":"Закрыть",0,1,2);
              rdsAdditionalContextMenuItemEx(NULL,RDS_MENU_DIVIDER,0,0);
              rdsAdditionalContextMenuItemEx("Открыть все",0,2,1);
              rdsAdditionalContextMenuItemEx("Закрыть все",0,2,0);
              rdsAdditionalContextMenuItemEx("Переключить все",0,2,2);
            break;

            // Выбор пункта меню пользователем
            case RDS_BFM_MENUFUNCTION:
              menu=(RDS_PMENUFUNCDATA)ExtParam; // Данные пункта меню
              switch(menu->Function)
                { case 1: // Открыть/закрыть
                    bypass=!bypass;         // Переключить режим
                    out=bypass?x_ext:x_int; // Подать на выход
                    Ready=1;                // Взвести флаг готовности
                    break;
                  case 2: // Один из новых пунктов
                    // Заполняем структуру параметров функции
                    callparams.Command=menu->MenuData;
                    callparams.servSize=sizeof(callparams);
                    // Вызываем у всех блоков родительской подсистемы
                    rdsBroadcastFunctionCallsEx(BlockData->Parent,
                                                EditCtrlFuncSet,
                                                &callparams,
                                                0);
                    break;
                }
              break;

            // Вызов функции блока
            case RDS_BFM_FUNCTIONCALL:
              // Приведение ExtParam к правильному типу
              func=(RDS_PFUNCTIONCALLDATA)ExtParam;
              // Проверяем, какая функция вызвана
              if(func->Function==EditCtrlFuncSet)
                { // Вызвана нужная нам функция
                  TProgGuideEditCtrlSetParams *params;
                  // Приводим указатель на параметры к нужному типу
                  // и проверяем размер переданной структуры
                  params=(TProgGuideEditCtrlSetParams*)(func->Data);
                  if(params->servSize<sizeof(TProgGuideEditCtrlSetParams))
                    break; // Размер недостаточен
                  // В зависимости от Command меняем режим блока
                  switch(params->Command)
                    { case 0: bypass=1; break;       // Закрыть
                      case 1: bypass=0; break;       // Открыть
                      case 2: bypass=!bypass; break; // Переключить
                      default: return RDS_BFR_DONE;  // Ошибка
                    }
                  // Взводим сигнал готовности и передаем нужный вход
                  // на выход
                  Ready=1;
                  out=bypass?x_ext:x_int;
                }
              break;
          }

        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef bypass
      #undef out
      #undef x_int
      #undef x_ext
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================