Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, добавляющих собственные пункты в контекстное меню блока. Библиотека содержит две модели:
- SimpleJoystick – двухкоординатная рукоятка, которую пользователь может перемещать мышью, с возможностью фиксировать одну из координат (изменения относительно предыдущей версии модели из §2.12.2 выделены цветом);
- 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; } //========= Конец главной функции ========= //============================================================================ // Двухкоординатная рукоятка //============================================================================ //====== Класс личной области данных ====== 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); // Конструктор класса 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); } //=========================================// Функция модели блока 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;} return RDS_BFR_DONE; // Отмена макроопределений #undef y #undef x #undef Ready #undef Start #undef pStart } //========================================= //============================================================================ // Блок с "окном" //============================================================================ 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; switch(CallMode) { // Проверка типов статических переменных 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); break;// Выбор пункта меню пользователем case RDS_BFM_MENUFUNCTION: bypass=!bypass; // Переключить режим out=bypass?x_ext:x_int; // Подать на выход один из входов Ready=1; // Взвести флаг готовности break;} return RDS_BFR_DONE; // Отмена макроопределений #undef bypass #undef out #undef x_int #undef x_ext #undef Ready #undef Start #undef pStart } //=========================================