Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, реагирующих на действия пользователя в режиме редактирования. Библиотека содержит три модели:
- OpenSysWin – модель блока, добавляющего в главное меню RDS пункт для открытия родительской подсистемы (см. §2.12.7, теперь окно настроек автоматически открывается при добавлении блока из библиотеки);
- TestBlkMoveSetter – модель блока, записывающего в динамические переменные значения скорости и угла поворота (см. §2.6.4, теперь при удалении этого блока пользователем выводится предупреждение о том, что другие блоки потеряют с ном связь);
- TestBlkMoveObject – модель блока, перемещающегося в окне подсистемы со скоростью и в направлении, считанными из динамических переменных (изменений в модели нет);
- SimpleJoystick – модель блока, изображающего двухкоординатную рукоятку, которую пользователь может перемещать мышью (см. §2.12.2, теперь блок все время остается квадратным).
Изменения относительно предыдущих версий этих моделей выделены цветом.
// Реакция на действия пользователя в режиме редактирования #include <windows.h> #include <math.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; } //========= Конец главной функции ========= //========================================================================= // Реакция на ручную вставку блока //========================================================================= // Класс личной области данных блока class TOpenSysWinData { public: RDS_MENUITEM MenuItem; // Идентификатор созданного пункта меню // Настроечные параметры char *Caption; // Название пункта int Key; // Клавиша (или 0,если ее нет) DWORD KeyShifts; // Состояние Ctrl, Alt и Shift // Создание пункта меню с заданными параметрами или // изменение параметров уже созданного пункта void RegisterMenuItem(void); int Setup(void); // Открыть окно настройки void SaveBin(void); // Сохранить параметры int LoadBin(void); // Загрузить параметры // Конструктор класса TOpenSysWinData(void) { Caption=NULL; Key=0; KeyShifts=0; MenuItem=NULL; }; // Деструктор класса ~TOpenSysWinData() { // Освободить память, занятую строкой Caption rdsFree(Caption); // Удалить пункт меню rdsUnregisterMenuItem(MenuItem); }; }; //========================================= // Создание или модификация пункта меню // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TOpenSysWinData::RegisterMenuItem(void) { // Вспомогательная переменная для заголовка пункта меню: // если Caption==NULL, пункт получит заголовок "Открыть окно" const char *text=Caption?Caption:"Открыть окно"; // Флаги пункта меню: если клавиша определена (Key!=0), // пункт будет иметь "горячую клавишу" DWORD options=Key?RDS_MENU_SHORTCUT:0; if(MenuItem) // Пункт уже есть - изменяем rdsChangeMenuItem(MenuItem,text,options,Key,KeyShifts,0,0); else // Пункта еще нет - создаем MenuItem=rdsRegisterMenuItem(text,options,Key,KeyShifts,0,0); } //========================================= // Запись параметров блока void TOpenSysWinData::SaveBin(void) { BYTE tag; // Переменная для байта тега int len=Caption?strlen(Caption):0; // Длина строки Caption tag=1; // Тег 1 – название пункта меню rdsWriteBlockData(&tag,sizeof(tag)); rdsWriteBlockData(&len,sizeof(len)); // Длина строки if(len) // При ненулевой длине – запись самой строки rdsWriteBlockData(Caption,len); tag=2; // Тег 2 – "горячая клавиша" rdsWriteBlockData(&tag,sizeof(tag)); rdsWriteBlockData(&Key,sizeof(Key)); rdsWriteBlockData(&KeyShifts,sizeof(KeyShifts)); tag=0; // Тег 0 – конец данных rdsWriteBlockData(&tag,sizeof(tag)); } //========================================= // Загрузка параметров блока int TOpenSysWinData::LoadBin(void) { BYTE tag; // Переменная для байта тега int len; // Прежде всего, освобождаем память, которую занимало старое // название пункта меню – сейчас загрузится новое rdsFree(Caption); Caption=NULL; for(;;) { if(!rdsReadBlockData(&tag,sizeof(tag))) break; // Тег не считан – данные неожиданно кончились switch(tag) { case 0: // Конец данных RegisterMenuItem(); // Создаем или изменяем пункт меню return RDS_BFR_DONE; case 1: // Название пункта rdsReadBlockData(&len,sizeof(len)); // Читаем длину if(len) // Длина ненулевая – читаем len байтов строки { // Отводим место с учетом нуля в конце строки Caption=(char*)rdsAllocate(len+1); rdsReadBlockData(Caption,len); // Читаем строку Caption[len]=0; // Дописываем нулевой байт в конец } break; case 2: // Данные "горячей клавиши" rdsReadBlockData(&Key,sizeof(Key)); rdsReadBlockData(&KeyShifts,sizeof(KeyShifts)); break; default: // Тег не опознан – ошибка return RDS_BFR_ERROR; } } // Вышли из цикла из-за неожиданного конца данных блока - ошибка return RDS_BFR_ERROR; } //========================================= // Настройка параметров блока // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" int TOpenSysWinData::Setup(void) { RDS_HOBJECT window; // Идентификатор объекта-окна BOOL ok; // Пользователь нажал "OK" // Создание окна window=rdsFORMCreate(FALSE,-1,-1,"Открытие окна"); // Поле ввода для строки названия rdsFORMAddEdit(window,0,1,RDS_FORMCTRL_EDIT, "Текст пункта меню:",200); rdsSetObjectStr(window,1,RDS_FORMVAL_VALUE,Caption); // Поле ввода для "горячей клавиши" rdsFORMAddEdit(window,0,2,RDS_FORMCTRL_HOTKEY, "Клавиша:",150); rdsSetObjectInt(window,2,RDS_FORMVAL_VALUE,Key); rdsSetObjectInt(window,2,RDS_FORMVAL_HKSHIFTS,KeyShifts); // Открытие окна ok=rdsFORMShowModalEx(window,NULL); if(ok) { // Нажата кнопка OK – запись параметров в блок rdsFree(Caption); // Уничтожение старой строки // Создание динамической копии введенной строки Caption=rdsDynStrCopy(rdsGetObjectStr(window,1, RDS_FORMVAL_VALUE)); // Чтение параметров клавиши Key=rdsGetObjectInt(window,2,RDS_FORMVAL_VALUE); KeyShifts=rdsGetObjectInt(window,2,RDS_FORMVAL_HKSHIFTS); // Создание пункта меню на основе изменившихся параметров RegisterMenuItem(); } // Уничтожение окна rdsDeleteObject(window); // Возвращаемое значение return ok?RDS_BFR_MODIFIED:RDS_BFR_DONE; } //========================================= // Блок, открывающий окно родительской подсистемы extern "C" __declspec(dllexport) int RDSCALL OpenSysWin(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Указатель на личную область данных блока,приведенный к // правильному типу TOpenSysWinData *data=(TOpenSysWinData*)(BlockData->BlockData); switch(CallMode) { case RDS_BFM_INIT: // Инициализация BlockData->BlockData=data=new TOpenSysWinData(); break; case RDS_BFM_CLEANUP: // Очистка delete data; break; case RDS_BFM_SETUP: // Настройка return data->Setup(); case RDS_BFM_SAVEBIN: // Сохранение параметров data->SaveBin(); break; case RDS_BFM_LOADBIN: // Загрузка параметров return data->LoadBin(); case RDS_BFM_MENUFUNCTION:// Выбор пункта меню rdsOpenSystemWindow(BlockData->Parent); break;case RDS_BFM_MANUALINSERT: // Добавление блока пользователем if( ((RDS_PMANUALINSERTDATA)ExtParam)->Single ) { if(data->Setup()) // Параметры изменены rdsSetModifiedFlag(TRUE);// Взвести флаг изменений } break;} return RDS_BFR_DONE; } //========================================= //========================================================================= // Реакция на удаление блока //========================================================================= //=========== Блок управления ============= // Структура личной области данных блока typedef struct { // Указатель на структуру подписки на скорость RDS_PDYNVARLINK VLink; // Указатель на структуру подписки на направление RDS_PDYNVARLINK ALink; } TestBlkMoveSetterData; //=========================================// Функция обратного вызова для подсчета подписчиков // динамической переменной BOOL RDSCALL DynVarUsersCountEnum(RDS_BHANDLE /*block*/, RDS_PDYNVARLINK /*link*/,LPVOID ptr) { // Приводим ptr к типу "указатель на целое" int *pCount=(int*)ptr; (*pCount)++; // Увеличиваем счетчик return TRUE; } //=========================================// Подсчет числа подписчиков на динамическую переменную int CountDynVarUsers(RDS_PDYNVARLINK link) { int count=0; // Перебрать всех подписчиков переменной link rdsEnumDynVarSubscribers(link,DynVarUsersCountEnum,&count); return count; } //=========================================// Модель блока управления extern "C" __declspec(dllexport) int RDSCALL TestBlkMoveSetter(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define Speed (*((double *)(pStart+2*RDS_VSZ_S))) #define Angle (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D))) // Вспомогательная переменная – указатель на личную область // данных блока, приведенный к правильному типу TestBlkMoveSetterData *privdata; switch(CallMode) { // Инициализация блока case RDS_BFM_INIT: // Отведение памяти под личную область данных privdata=(TestBlkMoveSetterData*) malloc(sizeof(TestBlkMoveSetterData)); BlockData->BlockData=privdata; // Создание переменной для передачи скорости privdata->VLink=rdsCreateAndSubscribeDV( RDS_DVPARENT, // В родительской "BlkMove_Speed",// Имя переменной "D", // Тип double TRUE, // Запрет удаления NULL); // Без нач.значения // Создание переменной для передачи направления privdata->ALink=rdsCreateAndSubscribeDV( RDS_DVPARENT, "BlkMove_Angle", "D", TRUE, NULL); break; // Очистка данных блока case RDS_BFM_CLEANUP: // Приведение указателя на личную область данных к // правильному типу privdata=(TestBlkMoveSetterData*)(BlockData->BlockData); // Удаление динамических переменных rdsDeleteDVByLink(privdata->VLink); rdsDeleteDVByLink(privdata->ALink); // Освобождение отведенной памяти free(privdata); break; // Проверка типа статических переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSDD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Выполнение такта моделирования case RDS_BFM_MODEL: // Приведение указателя на личную область данных к // правильному типу privdata=(TestBlkMoveSetterData*)(BlockData->BlockData); // Проверка существования переменной направления if(privdata->ALink!=NULL && privdata->ALink->Data!=NULL) { // Переменная существует – привести к типу double* double *pa=(double*)privdata->ALink->Data; if(*pa!=Angle) // Значение направления изменилось { // Записать значение в динамическую переменную *pa=Angle; // Уведомить всех подписчиков об изменении rdsNotifyDynVarSubscribers(privdata->ALink); } } // Проверка существования переменной скорости if(privdata->VLink!=NULL && privdata->VLink->Data!=NULL) { // Переменная существует – привести к типу double* double *pv=(double*)privdata->VLink->Data; if(*pv!=Speed) { // Записать значение в динамическую переменную *pv=Speed; // Уведомить всех подписчиков об изменении rdsNotifyDynVarSubscribers(privdata->VLink); } } break;// Удаление блока пользователем case RDS_BFM_MANUALDELETE: // Приведение указателя на личную область данных к // правильному типу privdata=(TestBlkMoveSetterData*)(BlockData->BlockData); // Если блок удаляется в составе подсистемы, // предупреждать не нужно if( ((RDS_PMANUALDELETEDATA)ExtParam)->WithSys) break; // Считаем число подписчиков на переменные этого блока if(CountDynVarUsers(privdata->VLink)>1 || CountDynVarUsers(privdata->ALink)>1) // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" rdsMessageBox("Внимание! Этот блок предоставляет " "данные другим блокам. Его удаление " "приведет к их отключению.", BlockData->BlockName, MB_OK | MB_ICONWARNING); break;} return RDS_BFR_DONE; // Отмена макроопределений #undef Angle #undef Speed #undef Ready #undef Start #undef pStart } //========================================= //============ Подвижный блок ============= // Структура личной области данных блока typedef struct { // Указатель на структуру подписки на скорость RDS_PDYNVARLINK VLink; // Указатель на структуру подписки на направление RDS_PDYNVARLINK ALink; // Указатель на структуру подписки на время RDS_PDYNVARLINK Time; // Дополнительные поля для хранения значений синуса // и косинуса направления движения блока double SinA; double CosA; } TestBlkMoveObjectData; // Модель перемещающегося блока extern "C" __declspec(dllexport) int RDSCALL TestBlkMoveObject(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 (*((double *)(pStart+2*RDS_VSZ_S))) #define y (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D))) #define a (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D))) #define t0 (*((double *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_D))) // Вспомогательная переменная – указатель на личную область // данных блока, приведенный к правильному типу TestBlkMoveObjectData *privdata; // Вспомогательные переменные для скорости и времени double v,time; switch(CallMode) { // Инициализация блока case RDS_BFM_INIT: // Отведение памяти под личную область данных privdata=(TestBlkMoveObjectData*) malloc(sizeof(TestBlkMoveObjectData)); BlockData->BlockData=privdata; // Подписка на переменную скорости privdata->VLink=rdsSubscribeToDynamicVar( RDS_DVPARENT, // В родительской "BlkMove_Speed", // Имя переменной "D", // Тип double FALSE); // Без поиска // Подписка на переменную направления privdata->ALink=rdsSubscribeToDynamicVar( RDS_DVPARENT, // В родительской "BlkMove_Angle", // Имя переменной "D", // Тип double FALSE); // Без поиска // Подписка на переменную времени privdata->Time=rdsSubscribeToDynamicVar( RDS_DVPARENT, // В родительской "DynTime", // Имя переменной "D", // Тип double TRUE); // Поиск в иерархии break; // Очистка данных блока case RDS_BFM_CLEANUP: // Приведение указателя на личную область данных к правильному типу privdata=(TestBlkMoveObjectData*)(BlockData->BlockData); // Прекращение подписки на все динамические переменные rdsUnsubscribeFromDynamicVar(privdata->VLink); rdsUnsubscribeFromDynamicVar(privdata->ALink); rdsUnsubscribeFromDynamicVar(privdata->Time); // Освобождение отведенной памяти free(privdata); break; // Проверка типа статических переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSDDDD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Реакция на изменение динамической переменной case RDS_BFM_DYNVARCHANGE: // Приведение указателя на личную область данных к правильному типу privdata=(TestBlkMoveObjectData*)(BlockData->BlockData); // Проверка – удалось ли подписаться на все переменные // (если хотя бы один указатель – NULL, значит, не удалось) if(privdata->VLink==NULL || // Скорость privdata->ALink==NULL || // Направление privdata->Time==NULL) // Время break; // Проверка – существуют ли все переменные // (если хотя бы один указатель – NULL, значит, не удалось) if(privdata->VLink->Data==NULL || // Скорость privdata->ALink->Data==NULL || // Направление privdata->Time->Data==NULL) // Время break; // Если изменилась переменная направления, нужно вычислить // и запомнить новые значения ыинуса и косинуса угла if(ExtParam==(void*)(privdata->ALink)) { // Изменилось направление – привести к типу double* double *pa=(double*)privdata->ALink->Data; // Значение угла в радианах a=(*pa)*M_PI/180.0; // Значения синуса и косинуса privdata->SinA=sin(a); privdata->CosA=cos(a); } // Записать во вспомогательные переменные v и time значения // скорости и времени v=*(double*)privdata->VLink->Data; time=*(double*)privdata->Time->Data; // Вычислить новое смещение изображения блока x+=v*(time-t0)*privdata->CosA; y-=v*(time-t0)*privdata->SinA; // Запомнить значение времени, для которого вычислены // новые координаты t0=time; break; } return RDS_BFR_DONE; // Отмена макроопределений #undef t0 #undef a #undef y #undef x #undef Ready #undef Start #undef pStart } //========================================= //============================================================================ // Реакция на изменение размеров блока (в двухкоординатной рукоятке) //============================================================================ //====== Класс личной области данных ====== class TSimpleJoystick { private: // Центр круга (рукоятки) до начала перетаскивания int OldHandleX,OldHandleY; // Координаты курсора на момент начала перетаскивания int OldMouseX,OldMouseY; // Флаги фиксации одной из координат BOOL LockX,LockY; // Идентификаторы добавленных пунктов меню RDS_MENUITEM MenuLockX,MenuLockY; public: // Настроечные параметры блока COLORREF BorderColor; // Цвет рамки блока COLORREF FieldColor; // Цвет прямоугольника COLORREF HandleColor; // Цвет круга в покое COLORREF MovingHandleColor; // Цвет круга при таскании COLORREF GrayedColor; // Цвет недоступной области int HandleSize; // Диаметр круга // Реакция на нажатие кнопки мыши int MouseDown(RDS_PMOUSEDATA mouse,double x,double y,DWORD *pFlags); // Реакция на перемещение курсора мыши void MouseMove(RDS_PMOUSEDATA mouse,double *px,double *py); // Рисование изображения блока void Draw(RDS_PDRAWDATA draw,double x,double y,BOOL moving); // Реакция на выбор добавленного пункта меню void MenuFunction(RDS_PMENUFUNCDATA MenuData); // Конструктор класса TSimpleJoystick(void) { BorderColor=0; // Черная рамка FieldColor=0xffffff; // Белое поле HandleColor=0xff0000; // Синий круг MovingHandleColor=0xff; // Красный при таскании HandleSize=20; // Диаметр круга GrayedColor=0x7f7f7f; // Серый LockX=LockY=FALSE; // Фиксация выключена // Создание пунктов меню // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" MenuLockX=rdsRegisterContextMenuItem("Фиксировать X",1,0); MenuLockY=rdsRegisterContextMenuItem("Фиксировать Y",2,0); }; // Деструктор класса ~TSimpleJoystick() { // Уничтожение пунктов меню rdsUnregisterMenuItem(MenuLockX); rdsUnregisterMenuItem(MenuLockY); }; }; //========================================= // Рисование изображения блока void TSimpleJoystick::Draw(RDS_PDRAWDATA draw, double x,double y,BOOL moving) { int hx,hy,cx,cy; RECT r; int hR=HandleSize*draw->DoubleZoom/2; // Радиус круга-рукоятки // Если размер блока - нулевой, рисовать негде if(draw->Height==0 || draw->Width==0) return; // Рисование поля блока rdsXGSetPenStyle(0,PS_SOLID,1,BorderColor,R2_COPYPEN); rdsXGSetBrushStyle(0,RDS_GFS_SOLID,FieldColor); rdsXGRectangle(draw->Left,draw->Top, draw->Left+draw->Width,draw->Top+draw->Height); // Вычисление центра прямоугольника блока cx=draw->Left+draw->Width/2; cy=draw->Top+draw->Height/2; // Вычисление координат центра круга-рукоятки hx=cx+x*draw->Width/2; hy=cy-y*draw->Height/2; // Установка области отсечения r.left=draw->Left+1; r.top=draw->Top+1; r.right=draw->Left+draw->Width-1; r.bottom=draw->Top+draw->Height-1; rdsXGSetClipRect(&r); // Рисование ограничений if(LockX||LockY) // Фиксируется одна из координат { // Установка серого цвета заливки rdsXGSetBrushStyle(0,RDS_GFS_SOLID,GrayedColor); if(LockX) // Фиксируется X { rdsXGFillRect(r.left,r.top,hx-hR,r.bottom); // Слева rdsXGFillRect(hx+hR,r.top,r.right,r.bottom); // Справа } else // Фиксируется Y { rdsXGFillRect(r.left,r.top,r.right,hy-hR); // Сверху rdsXGFillRect(r.left,hy+hR,r.right,r.bottom);// Снизу } } // Линии перекрестия rdsXGMoveTo(cx,draw->Top); rdsXGLineTo(cx,draw->Top+draw->Height); rdsXGMoveTo(draw->Left,cy); rdsXGLineTo(draw->Left+draw->Width,cy); // Рисование круга (цвет зависит от параметра moving) rdsXGSetPenStyle(RDS_GFSTYLE,PS_NULL,0,0,0); rdsXGSetBrushStyle(0,RDS_GFS_SOLID, moving?MovingHandleColor:HandleColor); rdsXGEllipse(hx-hR,hy-hR,hx+hR+1,hy+hR+1); // Отмена отсечения rdsXGSetClipRect(NULL); } //========================================= // Реакция на нажатие кнопки мыши int TSimpleJoystick::MouseDown(RDS_PMOUSEDATA mouse, double x,double y,DWORD *pFlags) { int hx,hy,cx,cy, hR=HandleSize*mouse->DoubleZoom/2; // Радиус круга // Если размер - нулевой, реакция не имеет смысла if(mouse->Height==0 || mouse->Width==0) return RDS_BFR_DONE; // Если нажата не левая кнопка, перетаскивать не надо // Разрешаем в этом случае вызов контекстного меню блока if(mouse->Button!=RDS_MLEFTBUTTON) return RDS_BFR_SHOWMENU; // Координаты цента блока cx=mouse->Left+mouse->Width/2; cy=mouse->Top+mouse->Height/2; // Координаты центра круга-рукоятки hx=cx+x*mouse->Width/2; hy=cy-y*mouse->Height/2; // Проверка попадания курсора в круг if(abs(mouse->x-hx)<=hR && abs(mouse->y-hy)<=hR) { // Курсор попал в круг // Запоминаем координаты центр круга на момент // начала перетаскивания OldHandleX=hx; OldHandleY=hy; // Координаты курсора на начало перетаскивания OldMouseX=mouse->x; OldMouseY=mouse->y; // Взводим флаг захвата мыши *pFlags|=RDS_MOUSECAPTURE; } // Курсор не попал в рукоятку - захватывать мышь // и подготавливать перетаскивание не нужно return RDS_BFR_DONE; } //========================================= // Реакция на перемещение курсора мыши void TSimpleJoystick::MouseMove(RDS_PMOUSEDATA mouse, double *px,double *py) { int hx,hy,cx,cy; // Если размер - нулевой, реакция не имеет смысла if(mouse->Height==0 || mouse->Width==0) { *px=*py=0.0; return; } // Новые координаты центра рукоятки hx=OldHandleX+(mouse->x-OldMouseX); hy=OldHandleY+(mouse->y-OldMouseY); // Координаты центра блока cx=mouse->Left+mouse->Width/2; cy=mouse->Top+mouse->Height/2; // По новым координатам центра рукоятки вычисляем соответствующие // им вещественные значения выходов, ограничивая их // диапазоном [-1...1] if(!LockX) { *px=2.0*(hx-cx)/mouse->Width; if(*px>1.0) *px=1.0; else if(*px<-1.0) *px=-1.0; } if(!LockY) { *py=-2.0*(hy-cy)/mouse->Height; if(*py>1.0) *py=1.0; else if(*py<-1.0) *py=-1.0; } } //========================================= // Функция реакции на выбор одного из пунктов меню void TSimpleJoystick::MenuFunction(RDS_PMENUFUNCDATA MenuData) { switch(MenuData->Function) { case 1: // Выбран пункт "Фиксировать X" LockX=!LockX; // Переключаем флаг фиксации X LockY=FALSE; // Отключаем фиксацию Y break; case 2: // Выбран пункт "Фиксировать Y" LockY=!LockY; // Переключаем флаг фиксации Y LockX=FALSE; // Отключаем фиксацию X break; } // Установка галочек у пунктов меню в зависимости от // флагов фиксации координат rdsSetMenuItemOptions(MenuLockX,LockX?RDS_MENU_CHECKED:0); rdsSetMenuItemOptions(MenuLockY,LockY?RDS_MENU_CHECKED:0); } //=========================================// Реакция на изменение размеров блока void TSimpleJoystick::Resizing(RDS_PRESIZEDATA ResData) { if(ResData->HorzResize && (!ResData->VertResize)) // Перетаскивается левый или правый маркер ResData->newHeight=ResData->newWidth; else if((!ResData->HorzResize) && ResData->VertResize) // Перетаскивается верхний или нижний маркер ResData->newWidth=ResData->newHeight; else // Перетаскивается угловой маркер или размер задан точно { // Вычисляем среднее арифметическое int avg=(ResData->newWidth+ResData->newHeight)/2; // Присваиваем его ширине и высоте ResData->newWidth=ResData->newHeight=avg; } } //=========================================// Функция модели блока extern "C" __declspec(dllexport) int RDSCALL SimpleJoystick(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 (*((double *)(pStart+2*RDS_VSZ_S))) #define y (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D))) // Вспомогательная переменная - указатель на личную область, //приведенный к правильному типу TSimpleJoystick *data=(TSimpleJoystick*)(BlockData->BlockData); switch(CallMode) { // Инициализация case RDS_BFM_INIT: BlockData->BlockData=new TSimpleJoystick(); break; // Очистка case RDS_BFM_CLEANUP: delete data; break; // Проверка допустимости типов переменных case RDS_BFM_VARCHECK: return strcmp((char*)ExtParam,"{SSDD}")? RDS_BFR_BADVARSMSG:RDS_BFR_DONE; // Нажатие кнопки мыши case RDS_BFM_MOUSEDOWN: return data->MouseDown((RDS_PMOUSEDATA)ExtParam,x,y, &(BlockData->Flags)); // Отпускание кнопки мыши case RDS_BFM_MOUSEUP: // Снятие захвата мыши RDS_SETFLAG(BlockData->Flags,RDS_MOUSECAPTURE,FALSE); break; // Перемещение курсора мыши case RDS_BFM_MOUSEMOVE: // Проверка: включен ли захват мыши if(BlockData->Flags & RDS_MOUSECAPTURE) // Включен { // Вызываем функцию реакции data->MouseMove((RDS_PMOUSEDATA)ExtParam,&x,&y); Ready=1; // Взводим сигнал готовности } break; // Рисование case RDS_BFM_DRAW: data->Draw((RDS_PDRAWDATA)ExtParam,x,y, BlockData->Flags & RDS_MOUSECAPTURE); break; // Выбор пользователем добавленного пункта меню case RDS_BFM_MENUFUNCTION: data->MenuFunction((RDS_PMENUFUNCDATA)ExtParam); break;// Изменение размеров блока case RDS_BFM_RESIZE: case RDS_BFM_RESIZING: data->Resizing((RDS_PRESIZEDATA)ExtParam); break;} return RDS_BFR_DONE; // Отмена макроопределений #undef y #undef x #undef Ready #undef Start #undef pStart } //=========================================