|
|
|
|
|
#include "zxLogInstance.h"
|
|
|
|
|
|
#include "iRibbonXmlCmd.h"
|
|
|
|
|
|
#include "zxSysUtils.h"
|
|
|
|
|
|
#include "ZxUiBase.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "ZxMainWindow.h"
|
|
|
|
|
|
#include "ZxTabWidget.h"
|
|
|
|
|
|
#include "TreeWxMain.h"
|
|
|
|
|
|
#include "iDockBaseWx.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "iModuleHelper.h"
|
|
|
|
|
|
#include "ZxDataWell.h"
|
|
|
|
|
|
#include "ZxDataProject.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "nmDemoFrameworkTest.h"
|
|
|
|
|
|
#include "iSubWndFitting.h"
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef MY_OWN_GRID
|
|
|
|
|
|
#include "iSubWndGrid.h"
|
|
|
|
|
|
#else
|
|
|
|
|
|
#include "nmDemoSubWnd.h"
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include "nmDemoWndUtils.h"
|
|
|
|
|
|
|
|
|
|
|
|
iSubWnd* nmDemoWndUtils::createSubWnd(const iRibbonXmlCmd* pCmdInfo, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd, \
|
|
|
|
|
|
bool& bCreatedNewOne)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (nullptr == pCmdInfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO 此处是自行控制是否单一模式
|
|
|
|
|
|
bool bOnlyOneForCurTabWx = true;
|
|
|
|
|
|
return makesureSubWnd(pCmdInfo->m_sID, pCmdInfo->m_sExtInfo, \
|
|
|
|
|
|
pMainWnd, bCreatedNewOne, \
|
|
|
|
|
|
bOnlyOneForCurTabWx);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
iSubWnd* nmDemoWndUtils::makesureSubWnd(QString sID, QString sExt, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd, \
|
|
|
|
|
|
bool& bCreatedNewOne, \
|
|
|
|
|
|
bool bOnlyOneForCurTabWx /*= true*/)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT (nullptr != pMainWnd);
|
|
|
|
|
|
|
|
|
|
|
|
//单一模式,则先查找,如果已经存在,则直接返回
|
|
|
|
|
|
if (bOnlyOneForCurTabWx)
|
|
|
|
|
|
{
|
|
|
|
|
|
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
|
|
|
|
|
|
ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx(); //仅仅在当前TabWx下查找,此处可以根据需要调整
|
|
|
|
|
|
if (nullptr != pTabWx)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < pTabWx->count(); i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
iSubWnd* pSubWnd = dynamic_cast<iSubWnd*>(pTabWx->widget(i));
|
|
|
|
|
|
if (nullptr != pSubWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_isSame(sID, pSubWnd->getWndID()))
|
|
|
|
|
|
{
|
|
|
|
|
|
bCreatedNewOne = false;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO 根据需要,是否需要激活当前
|
|
|
|
|
|
if (i != pTabWx->currentIndex())
|
|
|
|
|
|
{
|
|
|
|
|
|
pTabWx->setCurrentIndex(i);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return pSubWnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新的
|
|
|
|
|
|
bCreatedNewOne = true;
|
|
|
|
|
|
iSubWnd* pSubWnd = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
// Here is your own codes
|
|
|
|
|
|
int nID = sID.toInt();
|
|
|
|
|
|
if (nID == 5002)
|
|
|
|
|
|
{
|
|
|
|
|
|
#ifdef MY_OWN_GRID
|
|
|
|
|
|
pSubWnd = new iSubWndGrid(nullptr, sExt);
|
|
|
|
|
|
#else
|
|
|
|
|
|
pSubWnd = new nmDemoSubWnd(nullptr, sExt);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (nullptr != pSubWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
// TODO 此处根据需要设置 改变 标识
|
|
|
|
|
|
pSubWnd->setModified(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return pSubWnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::runCmdBySpecial(const iRibbonXmlCmd* pCmdInfo, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (nullptr == pCmdInfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 20250310 测试Release崩溃问题,暂时放开
|
|
|
|
|
|
//#ifdef QT_DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
// 专门提供给数值模块,进行一些框架代码的调试
|
|
|
|
|
|
if (_isSame(pCmdInfo->m_sID, "5999"))
|
|
|
|
|
|
{
|
|
|
|
|
|
nmDemoFrameworkTest::testFrmCodes(pCmdInfo, pMainWnd);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#endif
|
|
|
|
|
|
|
|
|
|
|
|
return runCmdBySpecial(pCmdInfo->m_sID, pCmdInfo->m_sExtInfo, pMainWnd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::runCmdBySpecial(QString sID, QString sExt, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Here is your own codes
|
|
|
|
|
|
if (_isSame(sID, "12345"))
|
|
|
|
|
|
{
|
|
|
|
|
|
QMessageBox::information(nullptr, zxAppDescEN, QObject::tr("I am abc"));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::isEnableOfID_Common(QString sID, QString sName, \
|
|
|
|
|
|
const bool bLicensed, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 20250310 测试Release崩溃问题,暂时放开
|
|
|
|
|
|
//#ifdef QT_DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
// 专门提供给数值模块,进行一些框架代码的调试
|
|
|
|
|
|
if (_isSame(sID, "5999"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return (true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#endif
|
|
|
|
|
|
|
|
|
|
|
|
bool b1 = (nullptr != zxCurProject);
|
|
|
|
|
|
// Here is your own codes
|
|
|
|
|
|
if (_isSame(sID, "5101"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return (true);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (_isSame(sID, "5002")) //或者 _isSame(sName, "PropertyLoad")
|
|
|
|
|
|
{
|
|
|
|
|
|
// <Cmd ID="5002" Bounds="0,1,2,1" Name="PropertyLoad" Alias="属性导入"
|
|
|
|
|
|
// TODO 只有当前窗体为流动分析或者流动段选择的情况下才能点击
|
|
|
|
|
|
bool bOk = false;
|
|
|
|
|
|
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
|
|
|
|
|
|
if (nullptr != pMainWnd1)
|
|
|
|
|
|
{
|
|
|
|
|
|
iSubWnd* pSubWnd = pMainWnd1->getCurSubWnd(); //当前窗体
|
|
|
|
|
|
if (nullptr != pSubWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
QString sCurID = pSubWnd->getWndID();
|
|
|
|
|
|
bOk = (_isSame(sCurID, "3003") || _isSame(sCurID, "3004")
|
|
|
|
|
|
// || _isSame(sCurID, "6001") //TODO 这是试井设计页面,待定
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return bOk;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (_isSame(sName, "NmDemo"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return (b1 && bLicensed);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (_isSame(sName, "NmMap"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return (b1 && bLicensed);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::dealwithRibbonTab(iRibbonXmlTab* pTab, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false; //your own codes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
|
|
|
|
|
|
Q_ASSERT (nullptr != pSubWndF);
|
|
|
|
|
|
iDockBaseWx* pWxDockNm1 = pSubWndF->getNmDockWx(0);
|
|
|
|
|
|
iDockBaseWx* pWxDockNm2 = pSubWndF->getNmDockWx(1);
|
|
|
|
|
|
Q_ASSERT (nullptr != pWxDockNm1);
|
|
|
|
|
|
Q_ASSERT (nullptr != pWxDockNm2);
|
|
|
|
|
|
if (pWxDockNm1->widget() != nullptr && pWxDockNm2->widget() != nullptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true; //已经设定的话,则不再处理
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef QT_DEBUG //请根据需要调整
|
|
|
|
|
|
|
|
|
|
|
|
pWxDockNm1->setWindowTitle(tr("Dock1's Title")); //改变Title
|
|
|
|
|
|
pWxDockNm2->setWindowTitle(tr("Dock2's Title"));
|
|
|
|
|
|
|
|
|
|
|
|
QTextEdit* pWx1 = new QTextEdit("This is demo for upper");
|
|
|
|
|
|
QTextEdit* pWx2 = new QTextEdit("This is demo for lower");
|
|
|
|
|
|
// QTextEdit* pWx1 = new CustomParaWx("This is demo for upper");
|
|
|
|
|
|
// pWx1->...
|
|
|
|
|
|
Q_ASSERT (nullptr != pWx1);
|
|
|
|
|
|
Q_ASSERT (nullptr != pWx2);
|
|
|
|
|
|
|
|
|
|
|
|
pWxDockNm1->setWidget(pWx1);
|
|
|
|
|
|
pWxDockNm2->setWidget(pWx2);
|
|
|
|
|
|
|
|
|
|
|
|
//getAnaDockParas(pSubWnd);
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
return true; //your own codes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::getAnaDockParas(iSubWnd* pSubWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
|
|
|
|
|
|
Q_ASSERT (nullptr != pSubWndF);
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// 如下代码,请根据需要在合适位置借鉴参照编写
|
|
|
|
|
|
QMap<QString, QVariant> mapAnaParas;//当前解析解的参数
|
|
|
|
|
|
mapAnaParas.clear();
|
|
|
|
|
|
pSubWndF->getBrotherDockParas(mapAnaParas, false);
|
|
|
|
|
|
Q_ASSERT (mapAnaParas.count() > 0);
|
|
|
|
|
|
|
|
|
|
|
|
// 解析解边界类型
|
|
|
|
|
|
m_Bdy_Type oBdyType = BT_None;
|
|
|
|
|
|
QString sKey = "Bdy";
|
|
|
|
|
|
if (mapAnaParas.contains(sKey))
|
|
|
|
|
|
{
|
|
|
|
|
|
int n = mapAnaParas[sKey].toInt();
|
|
|
|
|
|
if (n <= (int)BT_Rect)
|
|
|
|
|
|
{
|
|
|
|
|
|
oBdyType = m_Bdy_Type(n);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 边界距离(是井点到四周的直线距离)
|
|
|
|
|
|
if (oBdyType == BT_Rect || oBdyType == BT_Circle)
|
|
|
|
|
|
{
|
|
|
|
|
|
VecDouble vecBdyDiss;
|
|
|
|
|
|
vecBdyDiss.clear();
|
|
|
|
|
|
int nCount = (oBdyType == BT_Rect ? 4 : 1);
|
|
|
|
|
|
for (int i = 1; i <= nCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
QString sRealBdyName = getBdyRealNameOf(i, mapAnaParas); //sRealBdyName为map中边界距离key的真实内容
|
|
|
|
|
|
sKey = sRealBdyName;
|
|
|
|
|
|
if (!sKey.isEmpty() && mapAnaParas.contains(sKey))
|
|
|
|
|
|
{
|
|
|
|
|
|
vecBdyDiss << mapAnaParas[sKey].toDouble();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT (false);//TODO
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 说明,对于BT_Rect,vecBdyDiss中存放的四个距离数值,依次为,上(ne)/下(se)/左(we)/右(ee)
|
|
|
|
|
|
// 对于BT_Circle,vecBdyDiss中存放的是圆形半径r
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString nmDemoWndUtils::getBdyRealNameOf(int xIndex, QMap<QString, QVariant>& map)
|
|
|
|
|
|
{
|
|
|
|
|
|
QStringList list;
|
|
|
|
|
|
list << "ne" << "se" << "we" << "ee"<< "re";
|
|
|
|
|
|
|
|
|
|
|
|
int n = -1;
|
|
|
|
|
|
for (int i = 0; i < list.count(); i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
QString s = list[i];
|
|
|
|
|
|
if (map.find(s) != map.end())
|
|
|
|
|
|
{
|
|
|
|
|
|
n++;
|
|
|
|
|
|
if (n == xIndex - 1)//TODO -1,从1开始
|
|
|
|
|
|
{
|
|
|
|
|
|
return s;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//m_sError = QString("Bdy name of 'x%1'' not found.").arg(xIndex);
|
|
|
|
|
|
//zxLogRunF(m_sError);
|
|
|
|
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::nmCalAndFresh(iSubWnd* pSubWnd, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::saveRsts(iSubWnd* pSubWnd, \
|
|
|
|
|
|
QString sRstCode, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT (nullptr != pSubWnd);
|
|
|
|
|
|
|
|
|
|
|
|
// 请根据需要确定,文件存储的路径
|
|
|
|
|
|
QString sDir = ZxBaseUtil::getCurWellDirOf("Nm");
|
|
|
|
|
|
|
|
|
|
|
|
// 如果需要保存某个窗体(继承自ZxUiBase,并且实现了序列化接口onSerialize/onDeserialize)
|
|
|
|
|
|
// 则可以参照如下进行
|
|
|
|
|
|
ZxUiBase* pWx = nullptr; //your own widget
|
|
|
|
|
|
if (nullptr != pWx)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 把窗体内容存为二进制
|
|
|
|
|
|
// 注意:saveAsByteArr是 iWxBase(PVT模块)中的接口,如果需要,请参考下面源码进行自行实现
|
|
|
|
|
|
//pWx->saveAsByteArr(ba);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果需要把其它如double数组、map等存入二进制,可以参照 ZxBaHelper 提供的接口
|
|
|
|
|
|
|
|
|
|
|
|
// 其它代码
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmDemoWndUtils::loadRsts(iSubWnd* pSubWnd, \
|
|
|
|
|
|
QString sRstCode, \
|
|
|
|
|
|
const ZxMainWindow* pMainWnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT (nullptr != pSubWnd);
|
|
|
|
|
|
|
|
|
|
|
|
// 请根据需要确定,文件存储的路径
|
|
|
|
|
|
QString sDir = ZxBaseUtil::getCurWellDirOf("Nm");
|
|
|
|
|
|
|
|
|
|
|
|
QByteArray ba = QByteArray();//这是你读取出来的内容
|
|
|
|
|
|
ZxUiBase* pWx = nullptr; //your own widget
|
|
|
|
|
|
if (nullptr != pWx && !ba.isNull())
|
|
|
|
|
|
{
|
|
|
|
|
|
// 根据二进制内容更新窗体内容
|
|
|
|
|
|
// 注意:loadFromByteArr是 iWxBase(PVT模块)中的接口,如果需要,请参考下面源码进行自行实现
|
|
|
|
|
|
//pWx->loadFromByteArr(ba);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果需要把二进制内容更新至其它如double数组、map等存入,可以参照 ZxBaHelper 提供的接口
|
|
|
|
|
|
|
|
|
|
|
|
// 其它代码
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果需要加载某个窗体(继承自ZxUiBase,并且实现了序列化接口onSerialize/onDeserialize)
|
|
|
|
|
|
// 则可以参照如下进行
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _SCAN_OLD_CODES_
|
|
|
|
|
|
|
|
|
|
|
|
bool iWxBase::saveAsByteArr(QByteArray& v)
|
|
|
|
|
|
{
|
|
|
|
|
|
QString sRoot = this->objectName() + "abc";
|
|
|
|
|
|
ZxXpfDoc xpf(sRoot);
|
|
|
|
|
|
ZxSerializer ser(&xpf, true);
|
|
|
|
|
|
onSerialize(&ser);
|
|
|
|
|
|
|
|
|
|
|
|
QDataStream out(&v, QIODevice::WriteOnly);
|
|
|
|
|
|
out.setVersion(QDataStream::Qt_4_8);
|
|
|
|
|
|
if (xpf.save(out))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool iWxBase::loadFromByteArr(QByteArray& v)
|
|
|
|
|
|
{
|
|
|
|
|
|
QDataStream in(&v, QIODevice::ReadOnly);
|
|
|
|
|
|
// quint32 nVer = ser->getDocDate();
|
|
|
|
|
|
// if (nVer > (quint32)(20180101))
|
|
|
|
|
|
// {
|
|
|
|
|
|
// in.setVersion(QDataStream::Qt_4_8);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
{
|
|
|
|
|
|
in.setVersion(QDataStream::Qt_4_8);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString sRoot = this->objectName() + "abc";
|
|
|
|
|
|
ZxXpfDoc xpf(sRoot);
|
|
|
|
|
|
if (!xpf.load(in))
|
|
|
|
|
|
{
|
|
|
|
|
|
zxLogRunW(tr("Failed to load ByteArray"));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZxSerializer ser(&xpf, false);
|
|
|
|
|
|
onDeserialize(&ser);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|