Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, иллюстрирующих сохранение и загрузку состояния части схемы. Библиотека содержит две модели:
- SaveLoadState – блок, сохраняющий и загружающий состояние своей родительской подсистемы по щелчку мыши;
- SimplePlot – ранее рассмотренная модель блока-графика, в которую добавлена поддержка сохранения и зарузки состояния (изменения в модели выделены цветом).
// Сохранение и загрузка состояния блоков #include <windows.h> #include <stdio.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; } //========= Конец главной функции ========= //========================================= // Запись и загрузка состояния //========================================= extern "C" __declspec(dllexport) int RDSCALL SaveLoadState(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { RDS_PMOUSEDATA mouse; // Указатель на личную обрасть данных блока (int) int *pSaveId=(int*)(BlockData->BlockData); switch(CallMode) { // Инициализация case RDS_BFM_INIT: // Создание личной области данных (одно число int) BlockData->BlockData=pSaveId=new int; // Исходное значение идентификатора: -1 (т.е. нет) *pSaveId=-1; break; // Очистка case RDS_BFM_CLEANUP: // Удаление сохраненного состояния (если есть) rdsDeleteSystemState(*pSaveId); // Уничтожение личной области delete pSaveId; break; // Нажатие кнопки мыши case RDS_BFM_MOUSEDOWN: mouse=(RDS_PMOUSEDATA)ExtParam; if(mouse->Button==RDS_MLEFTBUTTON) // Левая кнопка { RDS_BLOCKDESCRIPTION descr; descr.servSize=sizeof(descr); BOOL save=FALSE,load=FALSE; // Есть ли у блока картинка? rdsGetBlockDescription(BlockData->Block,&descr); if(descr.Flags & RDS_BDF_HASPICTURE) { // Картинка есть – смотрим идентификатор элемента // под курсором int pic_id=rdsGetMouseObjectId(mouse); if(pic_id>0) save=TRUE; else if(pic_id<0) load=TRUE; } else if(mouse->y<mouse->Top+mouse->Height/2) save=TRUE; // Картинки нет, верх блока else load=TRUE; // Картинки нет, низ блока if(save) // Сохраняем состояние *pSaveId=rdsSaveSystemState(BlockData->Parent, *pSaveId,TRUE,NULL); if(load) // Загружаем состояние { rdsLoadSystemState(*pSaveId,NULL); // Необходимо обновить окно подсистемы rdsRefreshBlockWindows(BlockData->Parent,TRUE); } } break; } return RDS_BFR_DONE; } //========================================= //============================================================================== // В график внесены изменения для поддержки сохранения и загрузки состояния //============================================================================== //========================================= // Простой график – личная область данных //========================================= class TSimplePlotData { private: // Запомненные координаты поля графика int Gr_x1,Gr_x2,Gr_y1,Gr_y2; // Масштаб окна на момент последнего рисования double OldZoom; // Индекс последнего нарисованного отсчета int LastDrawnIndex; // Настроечные параметры графика (цвета, шаг и т.п.) double TimeStep; // Шаг записи отсчетов RDS_SERVFONTPARAMS Font; // Шрифт чисел на осях COLORREF BorderColor; // Цвет рамки вокруг блока COLORREF FillColor; // Цвет фона блока COLORREF PlotBorderColor; // Цвет рамки поля графика COLORREF PlotFillColor; // Цвет фона поля графика COLORREF LineColor; // Цвет лини графика int LineWidth; // Толщина линии графика // Ось X double Xmin,Xmax; // Диапазон double XGridStep; // Шаг чисел на осях int XNumDecimal; // Дробная часть чисел на осях // Ось Y double Ymin,Ymax; // Диапазон double YGridStep; // Шаг чисел на осях int YNumDecimal; // Дробная часть чисел на осях // Массивы для хранения отсчетов графика double *Times; // Массив отсчетов времени double *Values; // Массив значений int Count; // Размер массивов int NextIndex; // Индекс для записи следующего значения double NextTime; // Время записи следующего значения RDS_PDYNVARLINK Time; // Связь с динамической переменной // времени ("DynTime") public: // Функция сброса запомненного масштаба последнего рисования void ResetCoords(void){OldZoom=-1.0;}; // Функция отведения массивов отсчетов void AllocateArrays(void); // Функция освобождения массивов отсчетов void ClearArrays(void); // Добавление очередной точки в массив отсчетов графика void AddPoint(double v); int Setup(void); // Функция настройки параметров void SaveText(void); // Функция сохранения параметров void LoadText(char *text); // Функция загрузки параметров void Draw(RDS_PDRAWDATA DrawData); // Функция рисования void DrawFast(RDS_PDRAWDATA DrawData); // Функция быстрого рисования // Рисование иконки при отсутствии доступа к DynTime void DrawAdditional(RDS_PDRAWDATA DrawData); // Поиск индекса отсчета, соответствующего времени t int FindTimeIndex(double t); // Вывод всплывающей полсказки void PopupHint(RDS_PPOPUPHINTDATA hintdata);void SaveState(void); // Функция записи состояния void LoadState(void); // Функция загрузки состоянияTSimplePlotData(void); // Конструктор класса ~TSimplePlotData(); // Деструктор класса }; //========================================= // Модель блока extern "C" __declspec(dllexport) int RDSCALL SimplePlot( 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))) // Указатель на личную область, приведенный к правильному типу TSimplePlotData *data=(TSimplePlotData*)(BlockData->BlockData); switch(CallMode) { // Инициализация блока case RDS_BFM_INIT: BlockData->BlockData=new TSimplePlotData(); break; // Очистка данных блока case RDS_BFM_CLEANUP: delete data; break; // Проверка типов статических переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Функция настройки параметров case RDS_BFM_SETUP: data->ResetCoords(); // Сброс запомненных значений return data->Setup(); // Загрузка параметров в текстовом формате case RDS_BFM_LOADTXT: data->LoadText((char*)ExtParam); data->ResetCoords(); // Сброс запомненных значений break; // Созранение параметров в текстовом формате case RDS_BFM_SAVETXT: data->SaveText(); break; // Изменение размеров блока case RDS_BFM_RESIZE: data->ResetCoords(); // Сброс запомненных значений break; // Рисование внешнего вида блока case RDS_BFM_DRAW: //data->Draw((RDS_PDRAWDATA)ExtParam); data->DrawFast((RDS_PDRAWDATA)ExtParam); break; // Дополнительное рисование case RDS_BFM_DRAWADDITIONAL: data->DrawAdditional((RDS_PDRAWDATA)ExtParam); break; // Запуск расчета case RDS_BFM_STARTCALC: if(((RDS_PSTARTSTOPDATA)ExtParam)->FirstStart) data->AllocateArrays(); // Первый запуск break; // Сброс расчета case RDS_BFM_RESETCALC: data->ClearArrays(); break; // Реакция на изменение динамической переменной case RDS_BFM_DYNVARCHANGE: data->AddPoint(x); break; // Всплывающая подсказка case RDS_BFM_POPUPHINT: data->PopupHint((RDS_PPOPUPHINTDATA)ExtParam); break; } return RDS_BFR_DONE; // Отмена макроопределений для переменных #undef x #undef Ready #undef Start #undef pStart } //========================================= // Конструктор класса личной области данных графика TSimplePlotData::TSimplePlotData(void) { // Инициализация OldZoom: значение -1 приведет к принудительному // вычислению Gr_x1,Gr_x2,Gr_y1 и Gr_y2 в ближайшем вызове // функции Draw OldZoom=-1.0; // Инициализация LastDrawnIndex LastDrawnIndex=-1; // Присвоение начальных значений параметрам TimeStep=0.1; // Шаг записи BorderColor=0; // Цвет рамки вокруг блока FillColor=0xffffff; // Цвет фона блока PlotBorderColor=0; // Цвет рамки окна графика и сетки PlotFillColor=0xffffff; // Цвет окна графика LineColor=0; // Цвет линии графика LineWidth=1; // Толщина линии графика // Параметры шрифта Font.servSize=sizeof(Font); strcpy(Font.Name,"Arial"); Font.SizePriority=FALSE; Font.Height=15; Font.Color=0; Font.Bold=Font.Italic=Font.Underline=Font.StrikeOut=FALSE; Font.CharSet=DEFAULT_CHARSET; // Диапазоны осей, шаг сетки, число десятичных знаков // в числах на осях Xmin=0.0; Xmax=10.0; XGridStep=5.0; XNumDecimal=0; Ymin=-1.0; Ymax=1.0; YGridStep=0.5; YNumDecimal=1; // Обнуление указателей на массивы и их размера // (массивы еще не отведены) Times=Values=NULL; Count=NextIndex=0; NextTime=Xmin; // Подписка на динамическую переменную времени Time=rdsSubscribeToDynamicVar( RDS_DVPARENT,// В родительской подсистеме "DynTime", // Имя переменной "D", // Тип переменной (double) TRUE); // Искать по иерархии } //========================================= // Деструктор класса TSimplePlotData::~TSimplePlotData() { rdsUnsubscribeFromDynamicVar(Time); // Прекратить подписку ClearArrays(); // Освободить массивы } //========================================= // Рисование внешнего вида блока void TSimplePlotData::Draw(RDS_PDRAWDATA DrawData) { // Вспомогательные переменные // int Gr_x1,Gr_x2,Gr_y1,Gr_y2; - эти переменные стали // полями класса int x1,y1,x2,y2,textheight,w1,w2; char buf[80]; // Рамка графика rdsXGSetPenStyle(0,PS_SOLID,1,BorderColor,R2_COPYPEN); rdsXGSetBrushStyle(0,RDS_GFS_SOLID,FillColor); rdsXGRectangle(DrawData->Left,DrawData->Top, DrawData->Left+DrawData->Width, DrawData->Top+DrawData->Height); // Необходимо вычислить координаты поля графика относительно // верхнего левого угла блока // Установка параметров шрифта с учетом масштаба rdsXGSetFontByParStr(&Font,DrawData->DoubleZoom); rdsXGGetTextSize("0",NULL,&textheight); if(DrawData->DoubleZoom!=OldZoom) // Масштаб изменен { // Зазор сверху – половина высоты цифры + 1 точка Gr_y1=textheight/2+1; // Зазор снизу – полная высота цифры + 1 точка Gr_y2=DrawData->Height-textheight-1; // Зазор слева – ширина самого длинного числа вертикальной // оси или половина ширины Xmin sprintf(buf," %.*lf ",YNumDecimal,Ymin); rdsXGGetTextSize(buf,&w1,NULL); // Ширина Ymin sprintf(buf," %.*lf ",YNumDecimal,Ymax); rdsXGGetTextSize(buf,&w2,NULL); // Ширина Ymax if(w2>w1) w1=w2; sprintf(buf," %.*lf ",XNumDecimal,Xmin); rdsXGGetTextSize(buf,&w2,NULL); // Ширина Xmin w2/=2; if(w2>w1) w1=w2; Gr_x1=w1; // Зазор справа – половина ширины Xmax sprintf(buf," %.*lf ",XNumDecimal,Xmax); rdsXGGetTextSize(buf,&w2,NULL); // Ширина Xmax w2/=2; Gr_x2=DrawData->Width-w2; // Запоминание нового масштаба OldZoom=DrawData->DoubleZoom; } // if(DrawData->DoubleZoom!=OldZoom) // Абсолютные (на рабочем поле) координаты поля графика x1=DrawData->Left+Gr_x1; x2=DrawData->Left+Gr_x2; y1=DrawData->Top+Gr_y1; y2=DrawData->Top+Gr_y2; if(x1>=x2 || y1>=y2) // Негде рисовать return; // Прямоугольник поля графика rdsXGSetPenStyle(0,PS_SOLID,1,PlotBorderColor,R2_COPYPEN); rdsXGSetBrushStyle(0,RDS_GFS_SOLID,PlotFillColor); rdsXGRectangle(x1,y1,x2,y2); // Установка пунктирного стиля линии rdsXGSetPenStyle(0,PS_DOT,1,PlotBorderColor,R2_COPYPEN); rdsXGSetBrushStyle(0,RDS_GFS_EMPTY,0); // Без заливки // Горизонтальная ось с сеткой for(double x=Xmin;x<=Xmax+XGridStep*0.5;x+=XGridStep) { // ix - координата линии на рабочем поле int ix=x1+(x-Xmin)*(x2-x1)/(Xmax-Xmin); if(ix>x1 && ix<x2) // Чертим вертикальную линию { rdsXGMoveTo(ix,y1); rdsXGLineTo(ix,y2); } // Вывод числа на оси под полем sprintf(buf,"%.*lf",XNumDecimal,x); rdsXGGetTextSize(buf,&w1,NULL); rdsXGTextOut(ix-w1/2,y2,buf); } // Вертикальная ось с сеткой for(double y=Ymin;y<=Ymax+YGridStep*0.5;y+=YGridStep) { // iy - координата линии на рабочем поле int iy=y2-(y-Ymin)*(y2-y1)/(Ymax-Ymin); if(iy>y1 && iy<y2) // Чертим горизонтальную линию { rdsXGMoveTo(x1,iy); rdsXGLineTo(x2,iy); } // Вывод числа на оси слева от поля sprintf(buf,"%.*lf ",YNumDecimal,y); rdsXGGetTextSize(buf,&w1,&textheight); rdsXGTextOut(x1-w1-2,iy-textheight/2,buf); } // Если массивы не пустые – рисовать график if(Count) { RECT r; // Установить область отсечения рисования по полю графика r.left=x1+1; r.top=y1+1; r.right=x2-1; r.bottom=y2-1; rdsXGSetClipRect(&r); // Установить сплошной стиль линии, заданный для // графика цвет и толщину линии с учетом масштаба rdsXGSetPenStyle(0,PS_SOLID, LineWidth*DrawData->DoubleZoom, LineColor,R2_COPYPEN); // Строим ломанную линию по отсчетам из массивов for(int i=0;i<NextIndex;i++) { // Преобразуем вещественные отсчеты в целочисленные // координаты на рабочем поле int ix=x1+(Times[i]-Xmin)*(x2-x1)/(Xmax-Xmin), iy=y2-(Values[i]-Ymin)*(y2-y1)/(Ymax-Ymin); if(i) // Не первая точка – строим линию от предыдущей rdsXGLineTo(ix,iy); else // Первая точка графика – делаем ее текущей rdsXGMoveTo(ix,iy); } // Запоминаем последний нарисованный отсчет LastDrawnIndex=NextIndex-1; // Отмена отсечения rdsXGSetClipRect(NULL); } } //========================================= // Рисование изменений void TSimplePlotData::DrawFast(RDS_PDRAWDATA DrawData) { int x1,y1,x2,y2; // Если RDS требует полного рисования, или полное рисование // еще не проводилось, вызывается старая функция Draw if(DrawData->FullDraw || DrawData->DoubleZoom!=OldZoom) { Draw(DrawData); return; } // Вычисление абсолютных координат поля графика x1=DrawData->Left+Gr_x1; x2=DrawData->Left+Gr_x2; y1=DrawData->Top+Gr_y1; y2=DrawData->Top+Gr_y2; if(x1>=x2 || y1>=y2) // Негде рисовать return; // Если массивы не пустые – рисовать график, начиная // с последней уже нарисованной точки if(Count) { RECT r; // Установить область отсечения рисования по полю графика r.left=x1+1; r.top=y1+1; r.right=x2-1; r.bottom=y2-1; rdsXGSetClipRect(&r); // Установить сплошной стиль линии, заданный для // графика цвет и толщину линии с учетом масштаба rdsXGSetPenStyle(0,PS_SOLID, LineWidth*DrawData->DoubleZoom, LineColor,R2_COPYPEN); // Проверка допустимости LastDrawnIndex – на всякий случай if(LastDrawnIndex<0) LastDrawnIndex=0; // Строим ломанную линию по отсчетам из массивов, // начиная с LastDrawIndex for(int i=LastDrawnIndex;i<NextIndex;i++) { int ix=x1+(Times[i]-Xmin)*(x2-x1)/(Xmax-Xmin), iy=y2-(Values[i]-Ymin)*(y2-y1)/(Ymax-Ymin); if(i!=LastDrawnIndex) rdsXGLineTo(ix,iy); else rdsXGMoveTo(ix,iy); } // Запоминаем последний нарисованный отсчет LastDrawnIndex=NextIndex-1; // Отмена отсечения rdsXGSetClipRect(NULL); } } //========================================= // Дополнительное рисование void TSimplePlotData::DrawAdditional(RDS_PDRAWDATA DrawData) { // Проверка доступа к переменной времени if(Time==NULL || Time->Data==NULL) // Доступа нет { int w,h; // Константа, указывающая на стандартную иконку RDS DWORD icon=RDS_STDICON_YELCIRCEXCLAM; // Определяем размер иконки и выводим ее в центре блока if(rdsXGGetStdIconSize(icon,&w,&h)) rdsXGDrawStdIcon(DrawData->Left+(DrawData->Width-w)/2, DrawData->Top+(DrawData->Height-h)/2, icon); } } //========================================= // Отведение памяти под массивы void TSimplePlotData::AllocateArrays(void) { // Сначала нужно очистить массивы, если они были отведены ранее ClearArrays(); // При нулевом или отрицательном шаге записи отсчетов // работа блока невозможна if(TimeStep<=0.0) return; // Вычисление требуемого числа отсчетов по диапазону оси // времени и шагу записи Count=(Xmax-Xmin)/TimeStep+1; // Число отсчетов должно быть положительным if(Count<=0) {Count=0; return; } // Отведение памяти – по Count чисел double Times=new double[Count]; Values=new double[Count]; // Первый свободный индкс массива - 0 NextIndex=0; // Момент записи отсчета – начало диапазона оси времени NextTime=Xmin; } //========================================= // Освобождение массивов void TSimplePlotData::ClearArrays(void) { if(Count) // Массивы были отведены { delete[] Times; delete[] Values; } // Обнуление указателей и Count Times=Values=NULL; Count=NextIndex=0; } //========================================= // Добавление отсчета в массив void TSimplePlotData::AddPoint(double v) { double t; if(NextIndex>=Count) // Весь массив заполнен return; if(Time==NULL || Time->Data==NULL) // Нет доступа к “DynTime" return; // Получение значения времени из “DynTime" t=*((double*)Time->Data); if(t<NextTime) // Еще не пришло время писать отсчет return; // Достигнуто время записи Values[NextIndex]=v; Times[NextIndex]=t; NextIndex++; // Следующий отсчет – в следующий индекс NextTime+=TimeStep; // Время записи следующего отсчета } //========================================= // Сохранение параметров в текстовом виде void TSimplePlotData::SaveText(void) { RDS_HOBJECT ini; char *str; // Создание объекта для работы с текстом ini=rdsINICreateTextHolder(TRUE); // Создание в тексте секции "[General]" rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"General"); // Запись в секцию различных параметров rdsINIWriteDouble(ini,"TimeStep",TimeStep); rdsINIWriteDouble(ini,"Xmin",Xmin); rdsINIWriteDouble(ini,"Xmax",Xmax); rdsINIWriteDouble(ini,"XGridStep",XGridStep); rdsINIWriteInt(ini,"XNumDecimal",XNumDecimal); rdsINIWriteDouble(ini,"Ymin",Ymin); rdsINIWriteDouble(ini,"Ymax",Ymax); rdsINIWriteDouble(ini,"YGridStep",YGridStep); rdsINIWriteInt(ini,"YNumDecimal",YNumDecimal); // Создание в тексте секции "[Visuals]" rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"Visuals"); // Запись в секцию различных параметров rdsINIWriteInt(ini,"BorderColor",(int)BorderColor); rdsINIWriteInt(ini,"FillColor",(int)FillColor); rdsINIWriteInt(ini,"PlotBorderColor",(int)PlotBorderColor); rdsINIWriteInt(ini,"PlotFillColor",(int)PlotFillColor); rdsINIWriteInt(ini,"LineColor",(int)LineColor); rdsINIWriteInt(ini,"LineWidth",LineWidth); // Преобразование описания шрифта в строку для сохранения str=rdsStructToFontText(&Font,NULL); // Запись строки с описанием шрифта rdsINIWriteString(ini,"Font",str); rdsFree(str); // Освобождение памяти, занятой строкой // Запись сформированного текста в файл схемы или буфер обмена rdsCommandObject(ini,RDS_HINI_SAVEBLOCKTEXT); // Уничтожение вспомогательного объекта rdsDeleteObject(ini); } //========================================= // Загрузка параметров в текстовом виде из строки text void TSimplePlotData::LoadText(char *text) { RDS_HOBJECT ini; char *str; // Создание объекта для работы с текстом ini=rdsINICreateTextHolder(TRUE); // Загрузка текста в объект rdsSetObjectStr(ini,RDS_HINI_SETTEXT,0,text); // Если в тексте есть секция "General", загрузить из нее данные if(rdsINIOpenSection(ini,"General")) { TimeStep=rdsINIReadDouble(ini,"TimeStep",TimeStep); Xmin=rdsINIReadDouble(ini,"Xmin",Xmin); Xmax=rdsINIReadDouble(ini,"Xmax",Xmax); XGridStep=rdsINIReadDouble(ini,"XGridStep",XGridStep); XNumDecimal=rdsINIReadInt(ini,"XNumDecimal",XNumDecimal); Ymin=rdsINIReadDouble(ini,"Ymin",Ymin); Ymax=rdsINIReadDouble(ini,"Ymax",Ymax); YGridStep=rdsINIReadDouble(ini,"YGridStep",YGridStep); YNumDecimal=rdsINIReadInt(ini,"YNumDecimal",YNumDecimal); } // Если в тексте есть секция "Visuals", загрузить из нее данные if(rdsINIOpenSection(ini,"Visuals")) { BorderColor=(COLORREF)rdsINIReadInt(ini,"BorderColor", (int)BorderColor); FillColor=(COLORREF)rdsINIReadInt(ini,"FillColor", (int)FillColor); PlotBorderColor=(COLORREF)rdsINIReadInt(ini, "PlotBorderColor",(int)PlotBorderColor); PlotFillColor=(COLORREF)rdsINIReadInt(ini,"PlotFillColor", (int)PlotFillColor); LineColor=(COLORREF)rdsINIReadInt(ini,"LineColor", (int)LineColor); LineWidth=(COLORREF)rdsINIReadInt(ini,"LineWidth",LineWidth); str=rdsINIReadString(ini,"Font","",NULL); if(str) rdsFontTextToStruct(str,NULL,&Font); } // Уничтожение вспомогательного объекта rdsDeleteObject(ini); } //========================================= // Функция настройки параметров блока // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" int TSimplePlotData::Setup(void) { RDS_HOBJECT window; BOOL ok; char *str; // Создание окна window=rdsFORMCreate(TRUE,-1,-1,"Простой график"); // Вкладка "Оси" rdsFORMAddTab(window,1,"Оси"); rdsFORMAddEdit(window,1,100, RDS_FORMCTRL_EDIT | RDS_FORMFLAG_LINE,"Шаг записи",50); rdsSetObjectDouble(window,100,RDS_FORMVAL_VALUE,TimeStep); // Текстовая метка без возможности ввода rdsFORMAddEdit(window,1,1,RDS_FORMCTRL_LABEL,"Ось X:",0); // Диапазон (два поля ввода в одной строке) rdsFORMAddEdit(window,1,2,RDS_FORMCTRL_RANGEEDIT,"Диапазон",90); rdsSetObjectDouble(window,2,RDS_FORMVAL_VALUE,Xmin); rdsSetObjectDouble(window,2,RDS_FORMVAL_RANGEMAX,Xmax); rdsFORMAddEdit(window,1,3,RDS_FORMCTRL_EDIT,"Шаг сетки",50); rdsSetObjectDouble(window,3,RDS_FORMVAL_VALUE,XGridStep); // Поле ввода со стрелками увеличения/уменьшения rdsFORMAddEdit(window,1,4, RDS_FORMCTRL_UPDOWN | RDS_FORMFLAG_LINE, "Дробная часть чисел",50); rdsSetObjectInt(window,4,RDS_FORMVAL_VALUE,XNumDecimal); rdsSetObjectInt(window,4,RDS_FORMVAL_UPDOWNMIN,0); rdsSetObjectInt(window,4,RDS_FORMVAL_UPDOWNMAX,5); rdsSetObjectInt(window,4,RDS_FORMVAL_UPDOWNINC,1); // Текстовая метка без возможности ввода rdsFORMAddEdit(window,1,5,RDS_FORMCTRL_LABEL,"Ось Y:",0); rdsFORMAddEdit(window,1,6,RDS_FORMCTRL_RANGEEDIT,"Диапазон",90); rdsSetObjectDouble(window,6,RDS_FORMVAL_VALUE,Ymin); rdsSetObjectDouble(window,6,RDS_FORMVAL_RANGEMAX,Ymax); rdsFORMAddEdit(window,1,7,RDS_FORMCTRL_EDIT,"Шаг сетки",50); rdsSetObjectDouble(window,7,RDS_FORMVAL_VALUE,YGridStep); rdsFORMAddEdit(window,1,8,RDS_FORMCTRL_UPDOWN, "Дробная часть чисел",50); rdsSetObjectInt(window,8,RDS_FORMVAL_VALUE,YNumDecimal); rdsSetObjectInt(window,8,RDS_FORMVAL_UPDOWNMIN,0); rdsSetObjectInt(window,8,RDS_FORMVAL_UPDOWNMAX,5); rdsSetObjectInt(window,8,RDS_FORMVAL_UPDOWNINC,1); // Вкладка "Внешний вид" rdsFORMAddTab(window,2,"Внешний вид"); rdsFORMAddEdit(window,2,9,RDS_FORMCTRL_COLOR, "Цвет рамки блока",50); rdsSetObjectInt(window,9,RDS_FORMVAL_VALUE,(int)BorderColor); rdsFORMAddEdit(window,2,10, RDS_FORMCTRL_COLOR | RDS_FORMFLAG_LINE,"Цвет фона блока",50); rdsSetObjectInt(window,10,RDS_FORMVAL_VALUE,(int)FillColor); rdsFORMAddEdit(window,2,11,RDS_FORMCTRL_COLOR, "Цвет рамки графика и сетки",50); rdsSetObjectInt(window,11,RDS_FORMVAL_VALUE, (int)PlotBorderColor); rdsFORMAddEdit(window,2,12,RDS_FORMCTRL_COLOR, "Цвет фона графика",50); rdsSetObjectInt(window,12,RDS_FORMVAL_VALUE,(int)PlotFillColor); rdsFORMAddEdit(window,2,13,RDS_FORMCTRL_COLOR, "Цвет линии графика",50); rdsSetObjectInt(window,13,RDS_FORMVAL_VALUE,(int)LineColor); rdsFORMAddEdit(window,2,14, RDS_FORMCTRL_UPDOWN | RDS_FORMFLAG_LINE, "Толщина линии графика",50); rdsSetObjectInt(window,14,RDS_FORMVAL_VALUE,LineWidth); rdsSetObjectInt(window,14,RDS_FORMVAL_UPDOWNMIN,0); rdsSetObjectInt(window,14,RDS_FORMVAL_UPDOWNMAX,5); rdsSetObjectInt(window,14,RDS_FORMVAL_UPDOWNINC,1); // Кнопка открытия диалога выбора шрифта rdsFORMAddEdit(window,2,15,RDS_FORMCTRL_FONTSELECT, "Шрифт чисел",0); // Преобразование шрифта в строку и занесение в поле ввода str=rdsStructToFontText(&Font,NULL); rdsSetObjectStr(window,15,RDS_FORMVAL_VALUE,str); rdsFree(str); // Открытие окна ok=rdsFORMShowModalEx(window,NULL); if(ok) { // Нажата кнопка OK - запись параметров обратно в блок Xmin=rdsGetObjectDouble(window,2,RDS_FORMVAL_VALUE); Xmax=rdsGetObjectDouble(window,2,RDS_FORMVAL_RANGEMAX); XGridStep=rdsGetObjectDouble(window,3,RDS_FORMVAL_VALUE); XNumDecimal=rdsGetObjectInt(window,4,RDS_FORMVAL_VALUE); TimeStep=rdsGetObjectDouble(window,100,RDS_FORMVAL_VALUE); Ymin=rdsGetObjectDouble(window,6,RDS_FORMVAL_VALUE); Ymax=rdsGetObjectDouble(window,6,RDS_FORMVAL_RANGEMAX); YGridStep=rdsGetObjectDouble(window,7,RDS_FORMVAL_VALUE); YNumDecimal=rdsGetObjectInt(window,8,RDS_FORMVAL_VALUE); BorderColor=(COLORREF)rdsGetObjectInt(window,9,RDS_FORMVAL_VALUE); FillColor=(COLORREF)rdsGetObjectInt(window,10,RDS_FORMVAL_VALUE); PlotBorderColor=(COLORREF)rdsGetObjectInt(window,11,RDS_FORMVAL_VALUE); PlotFillColor=(COLORREF)rdsGetObjectInt(window,12,RDS_FORMVAL_VALUE); LineColor=(COLORREF)rdsGetObjectInt(window,13,RDS_FORMVAL_VALUE); LineWidth=rdsGetObjectInt(window,14,RDS_FORMVAL_VALUE); // Получение параметров шрифта из строки str=rdsGetObjectStr(window,15,RDS_FORMVAL_VALUE); rdsFontTextToStruct(str,NULL,&Font); } // Уничтожение окна rdsDeleteObject(window); // Возвращаемое значение return ok?RDS_BFR_MODIFIED:RDS_BFR_DONE; } //========================================= // Всплывающая подсказка // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TSimplePlotData::PopupHint(RDS_PPOPUPHINTDATA hintdata) { int x1,x2,y1,y2,index; double t; char *text,*str_t,*str_v; // Проверка доступа к переменной времени if(Time==NULL || Time->Data==NULL) { rdsSetHintText("ОШИБКА: в схеме нет переменной DynTime"); return; } // Определение абсолютных координат поля графика x1=hintdata->Left+Gr_x1; x2=hintdata->Left+Gr_x2; y1=hintdata->Top+Gr_y1; y2=hintdata->Top+Gr_y2; // Если курсор мыши не попадает в поле графика, // подсказку выводить не нужно if(hintdata->x<x1 || hintdata->x>x2 || hintdata->y<y1 || hintdata->y>y2) return; // Преобразование экранной горизонтальной координаты // в значение времени согласно масштабу графика t=(hintdata->x-x1)*(Xmax-Xmin)/(x2-x1)+Xmin; // Поиск отсчета, соответствующего этому моменту времени index=FindTimeIndex(t); if(index<0) return; // Ошибка – отчет не найден // Преобразование времени и значения отсчета в строки str_t=rdsDtoA(Times[index],-1,NULL); str_v=rdsDtoA(Values[index],-1,NULL); // Формирование текста подсказки text=rdsDynStrCat("Время: ",str_t,FALSE); rdsAddToDynStr(&text,"\nЗначение: ",FALSE); rdsAddToDynStr(&text,str_v,FALSE); // Установка текста подсказки rdsSetHintText(text); // Освобождение динамических строк rdsFree(text); rdsFree(str_t); rdsFree(str_v); // Изменение параметров подсказки таким образом, чтобы при // смещении курсора на одну точку она вывелась снова hintdata->HZLeft=hintdata->x; hintdata->HZTop=hintdata->y; hintdata->HZWidth=hintdata->HZHeight=1; // Задержка гашения подсказки – одна минута hintdata->HideTimeout=60000; } //========================================= /* // Поиск отсчета, соответствующего времени t // (линейный поиск) int TSimplePlotData::FindTimeIndex(double t) { if(Count==0) // Массивы пусты return -1; if(t<=Times[0]) // t раньше начала массива return 0; // Ближайший индекс - 0 // Поиск первого индекса, большего t for(int i=1;i<NextIndex;i++) if(Times[i]>t) // t между i-1 и i { double d1=fabs(t-Times[i-1]), d2=fabs(t-Times[i]); return (d1<d2)?(i-1):i; // Возвращаем ближайший } // Ничего не нашли – значит, t>Times[NextIndex-1] return NextIndex-1; } //========================================= */ // Поиск отсчета, соответствующего времени t // (метод деления пополам) int TSimplePlotData::FindTimeIndex(double t) { int L,R; double dl,dr; if(Count==0 || NextIndex==0) // Нет данных в массивах return -1; if(NextIndex==1) // В массиве единственный отсчет return 0; // В массиве по крайней мере два значения – проверяем границы if(t<=Times[0])// t меньше первого отсчета return 0; if(t>=Times[NextIndex-1]) // t больше последнего отсчета return NextIndex-1; // t - внутри диапазона массива L=0; R=NextIndex-1; while(L<R-1) { int m=(L+R)/2; if(Times[m]<t) L=m; else R=m; } // t лежит между L и R dl=fabs(t-Times[L]), dr=fabs(t-Times[R]); return (dl<dr)?L:R; // Возвращаем ближайший } //=========================================// Запись состояния блока void TSimplePlotData::SaveState(void) { // Макрос для записи одной переменной #define WRITEBLOCKDATAVAR(v) rdsWriteBlockData(&v,sizeof(v)) WRITEBLOCKDATAVAR(TimeStep); WRITEBLOCKDATAVAR(Xmin); WRITEBLOCKDATAVAR(Xmax); WRITEBLOCKDATAVAR(XGridStep); // Запись массивов отсчетов rdsWriteBlockData(Times,Count*sizeof(double)); rdsWriteBlockData(Values,Count*sizeof(double)); WRITEBLOCKDATAVAR(NextIndex); WRITEBLOCKDATAVAR(NextTime); // Отмена макроса #undef WRITEBLOCKDATAVAR } //=========================================// Загрузка состояния блока void TSimplePlotData::LoadState(void) { // Макрос для загрузки одной переменной #define READBLOCKDATAVAR(v) rdsReadBlockData(&v,sizeof(v)) READBLOCKDATAVAR(TimeStep); READBLOCKDATAVAR(Xmin); READBLOCKDATAVAR(Xmax); READBLOCKDATAVAR(XGridStep); // Отведение массивов перед загрузкой AllocateArrays(); rdsReadBlockData(Times,Count*sizeof(double)); rdsReadBlockData(Values,Count*sizeof(double)); READBLOCKDATAVAR(NextIndex); READBLOCKDATAVAR(NextTime); // Отмена макроса #undef READBLOCKDATAVAR // Сброс параметров оптимизации рисования OldZoom=-1.0; LastDrawnIndex=-1; } //=========================================