/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// // ExDynamicBlocks.cpp : Defines the initialization routines for the DLL. // #include "StdAfx.h" #include "ExDynamicBlocksModule.h" #include "RxDynamicModule.h" #include "DbDatabase.h" #include "DbSSet.h" #include "DbBlockReference.h" #include "Ed/EdUserIO.h" #include "DbLine.h" #include "DbUserIO.h" #include "DbBlockTableRecord.h" #include "DbBlockTable.h" #include "OdRound.h" #include "DbDictionary.h" #include "DynamicBlocks/DbBlockElement.h" #include "DynamicBlocks/DbBlockAction.h" #include "DynamicBlocks/DbBlockGripExpr.h" #include "DynamicBlocks/DbBlockRepresentation.h" #include "DynamicBlocks/DbDynamicBlockPurgePreventer.h" #include "DbUnitsFormatter.h" #include "DbIdMapping.h" #include "DbXrecord.h" #include "SysVarInfo.h" #include "DbErrorInvalidSysvar.h" #include "DbSymUtl.h" #if defined(_TOOLKIT_IN_DLL_) && defined(_MSC_VER) // MacOS X dynamic library loading. //#ifdef _TOOLKIT_IN_DLL_ extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID ) { switch ( dwReason ) { case DLL_PROCESS_ATTACH: // remove this if you need per-thread initialization DisableThreadLibraryCalls( (HMODULE)hInstance ); break; } return TRUE; } #endif //_TOOLKIT_IN_DLL_ // MacOS X dynamic library loading. ODRX_DEFINE_DYNAMIC_MODULE(ExDynamicBlocksModule); ExDynamicBlocksModule::ExDynamicBlocksModule() { } ExDynamicBlocksModule::~ExDynamicBlocksModule() { } OdInt16 g_nBLOCKEDITOR = 0; static OdInt16 g_nBVMODE = 0; static OdResBufPtr get_var_BVMODE(const OdDbDatabase*) { return OdResBuf::newRb(OdResBuf::kRtInt16, g_nBVMODE); } static void set_var_BVMODE(OdDbDatabase*, const OdResBuf* pRbValue) { OdInt16 n = pRbValue->getInt16(); if (n != 0 && n != 1) throw OdError_InvalidSysvarValue(L"BVMODE", 0, 1); g_nBVMODE = n; } static OdResBufPtr get_var_BLOCKEDITOR(const OdDbDatabase*) { return OdResBuf::newRb(OdResBuf::kRtInt16, g_nBLOCKEDITOR); } static void set_var_BLOCKEDITOR(OdDbDatabase*, const OdResBuf* pRbValue) { OdInt16 n = pRbValue->getInt16(); if (n != 0 && n != 1) throw OdError_InvalidSysvarValue(L"BLOCKEDITOR", 0, 1); g_nBLOCKEDITOR = n; } void ExDynamicBlocksModule::initApp() { odedRegCmds()->addCommand(&_cmd1); odedRegCmds()->addCommand(&_cmd2); odedRegCmds()->addCommand(&_cmd3); odedRegCmds()->addCommand(&_cmd4); odedRegCmds()->addCommand(&_cmd5); odedRegCmds()->addCommand(&_cmd6); odedRegCmds()->addCommand(&_cmd7); odedRegCmds()->addCommand(&_cmd8); odedRegCmds()->addCommand(&_cmd9); odedRegCmds()->addCommand(&_cmd10); odedRegCmds()->addCommand(&_cmd11); odedRegCmds()->addCommand(&_cmd12); odedRegCmds()->addCommand(&_cmd13); odedRegCmds()->addCommand(&_cmd14); odedRegCmds()->addCommand(&_cmd15); odedRegCmds()->addCommand(&_cmd16); #ifdef DYNAMIC_GRIPS_TEST OdDbSBAppData::rxInit(); OdRxOverrule::setIsOverruling(true); OdRxOverrule::addOverrule(OdDbBlockReference::desc(), &_grips); #endif OdRxDictionaryPtr pSysVarDict = odrxSysRegistry()->getAt(L"ODDB_SYSVARDICT"); OdSmartPtr pVarInfo = OdRxObjectImpl::createObject(); pVarInfo->m_getFn = get_var_BVMODE; pVarInfo->m_setFn = set_var_BVMODE; pSysVarDict->putAt(L"BVMODE", pVarInfo); pVarInfo = OdRxObjectImpl::createObject(); pVarInfo->m_getFn = get_var_BLOCKEDITOR; pVarInfo->m_setFn = set_var_BLOCKEDITOR; pSysVarDict->putAt(L"BLOCKEDITOR", pVarInfo); } void ExDynamicBlocksModule::uninitApp() { #ifdef DYNAMIC_GRIPS_TEST OdDbSBAppData::rxUninit(); OdRxOverrule::removeOverrule(OdDbBlockReference::desc(), &_grips); #endif odedRegCmds()->removeGroup(L"DynamicBlocks"); OdRxDictionaryPtr pSysVarDict = odrxSysRegistry()->getAt(L"ODDB_SYSVARDICT"); pSysVarDict->remove(L"BVMODE"); pSysVarDict->remove(L"BLOCKEDITOR"); } // This helper function creates a new empty dynamic block with all the necessary properties set OdDbBlockTableRecordPtr createDynamicBlock(OdDbDatabase* db, const OdString& name) { auto newBlock = OdDbBlockTableRecord::createObject(); newBlock->setName(name); OdDbBlockTablePtr(db->getBlockTableId().safeOpenObject(OdDb::kForWrite))->add(newBlock); // add roundtrip name record (In the old versions main (template) block was also anonymous, now this xdata is used a a marker) db->newRegApp(AcDbDynamicBlockTrueName); OdResBufPtr xdata = OdResBuf::newRb(1001, AcDbDynamicBlockTrueName); xdata->setNext(OdResBuf::newRb(1000, name)); newBlock->setXData(xdata); // set block guid (to identify the block within block libraries) db->newRegApp(AcDbDynamicBlockGUID); xdata = OdResBuf::newRb(1001, AcDbDynamicBlockGUID); xdata->setNext(OdResBuf::newRb(1000, ::odrxSystemServices()->createGuid())); newBlock->setXData(xdata); // add roundtrip preventer (otherwise old versions may purge that block, because it may have no references (only representations)) newBlock->createExtensionDictionary(); OdDbDictionaryPtr xd = newBlock->extensionDictionary().safeOpenObject(OdDb::kForWrite); xd->setAt(L"AcDbDynamicBlockRoundtripPurgePreventer", OdDbDynamicBlockPurgePreventer::createObject()); // create an empty evaluation graph OdDbEvalGraph::createGraph(newBlock, ACAD_ENHANCEDBLOCK); // don't forget to call OdDbBlockRepresentationContext::tagEntitiesInBlock after filling the block return newBlock; } static OdString formatResbuf( const OdDbEvalVariant& rb ) { OdString s; switch (rb.getType()) { case kDwgReal: s.format(OD_T("%g"), rb.getDouble() ); break; case kDwgText: return rb.getString(); case kDwgInt16: s.format(OD_T("%d"), rb.getInt16() ); break; case kDwgInt32: s.format(OD_T("%d"), rb.getInt32() ); break; case kDwg3Real: { OdGePoint3d p = rb.getPoint3d(); s.format(OD_T("{%g,%g,%g}"), p.x, p.y, p.z ); } break; case kDwg2Real: { OdGePoint2d p = rb.getPoint2d(); s.format(OD_T("{%g,%g}"), p.x, p.y ); } break; } return s; } static OdString formatValue(OdDbDynBlockReferenceProperty* prop, OdDbDatabase* pDb) { return prop->unitsType() == OdDbDynBlockReferenceProperty::kAngular ? pDb->formatter().formatAngle(prop->value().getDouble()) : formatResbuf(prop->value()); } void ListPropertiesCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select( OD_T("Select dynamic block reference:") ); for ( OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next() ) { OdDbBlockReferencePtr br = OdDbBlockReference::cast(it->objectId().safeOpenObject()); if ( br.isNull() ) continue; OdDbDynBlockReference ref( br.get() ); if ( !ref.isDynamicBlock() ) continue; OdDbDynBlockReferencePropertyArray a; ref.getBlockProperties(a); for ( unsigned i = 0; i < a.size(); i++ ) { OdString s; s.format( OD_T("%ls (%ls,%ls), %ls = %ls"), a[i]->propertyName().c_str(), a[i]->show() ? OD_T("v") : OD_T("h"), a[i]->readOnly() ? OD_T("r") : OD_T("w"), a[i]->description().c_str(), formatValue(a[i], pDbCmdCtx->database()).c_str()); pIO->putString( s ); } } } void BlockMoveCmd::execute( OdEdCommandContext* pCmdCtx ) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbDatabasePtr pDb = pDbCmdCtx->database(); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select(OD_T("Select dynamic block reference:")); for (OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next()) { OdDbBlockReferencePtr br = OdDbBlockReference::cast(it->objectId().safeOpenObject()); if (br.isNull()) continue; OdDbDynBlockReference ref(br.get()); if (!ref.isDynamicBlock()) continue; OdDbDynBlockReferencePropertyArray a; ref.getBlockProperties(a); int xProp = -1; int yProp = -1; for (unsigned i = 0; i < a.size(); i++) { if (a[i]->propertyName() == OD_T("Position X")) xProp = i; if (a[i]->propertyName() == OD_T("Position Y")) yProp = i; } if (yProp == -1 || xProp == -1) continue; OdGePoint3d base = br->position(); base.x += a[xProp]->value().getDouble(); // should go to UCS really base.y += a[yProp]->value().getDouble(); OdGePoint3d p = pIO->getPoint(OD_T("Enter position"), OdEd::kInpThrowEmpty, &base); a[xProp]->setValue(p.x - br->position().x); a[yProp]->setValue(p.y - br->position().y); } } static int printProperties( const OdDbDynBlockReferencePropertyArray& a, OdDbCommandContext* pDbCmdCtx ) { unsigned j = 1; for (auto prop : a) { if (!prop->show() || prop->readOnly()) continue; OdString s; s.format(OD_T("%d. %ls, %ls = %ls"), j++, prop->propertyName().c_str(), prop->description().c_str(), formatValue(prop, pDbCmdCtx->database()).c_str()); pDbCmdCtx->userIO()->putString(s); } return j - 1; } static void modifyProperty(unsigned N, const OdDbDynBlockReferencePropertyArray& a, OdDbCommandContext* pDbCmdCtx) { OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); unsigned j = 1; for (auto prop : a) { if (!prop->show() || prop->readOnly()) continue; if (N != j++) continue; OdString s; s.format(OD_T("%ls = %ls"), prop->propertyName().c_str(), formatValue(prop, pDbCmdCtx->database()).c_str()); OdDbEvalVariantArray av; prop->getAllowedValues(av); if (!av.isEmpty()) { pIO->putString(OD_T("Allowed values:")); for (unsigned k = 0; k < av.size(); k++) { s.format(OD_T("%d. %ls"), k, formatResbuf(av[k]).c_str()); pIO->putString(s); } int n = pIO->getInt(OD_T("Select value:"), OdEd::kInpDefault, -1); if (n < 0 || n >= (int)av.size()) return; if (prop->setValue(av[n])) pIO->putString(OD_T("Value set successfully")); else pIO->putString(OD_T("Value set failed")); } else { OdDbEvalVariant newValue = prop->value(); auto prompt = OD_T("Enter new value:"); try { switch (newValue.getType()) { case kDwgReal: if (prop->unitsType() == OdDbDynBlockReferenceProperty::kAngular) newValue.setDouble(pIO->getAngle(prompt, OdEd::kInpThrowEmpty)); else newValue.setDouble(pIO->getReal(prompt, OdEd::kInpThrowEmpty)); break; case kDwgText: newValue.setString(pIO->getString(prompt, OdEd::kInpThrowEmpty)); break; case kDwgInt16: newValue.setInt16(OdInt16(pIO->getInt(prompt, OdEd::kInpThrowEmpty))); break; case kDwgInt32: newValue.setInt32(OdUInt32(pIO->getInt(prompt, OdEd::kInpThrowEmpty))); break; case kDwg3Real: newValue.setPoint3d(pIO->getPoint(prompt, OdEd::kInpThrowEmpty, &prop->value().getPoint3d())); break; } if (prop->setValue(newValue)) pIO->putString(OD_T("Value set successfully")); else pIO->putString(OD_T("Value set failed")); } catch (const OdEdEmptyInput&) {// do not change property } } return; } } void ModifyPropertiesCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select( OD_T("Select dynamic block reference:") ); for ( OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next() ) { OdDbBlockReferencePtr br = OdDbBlockReference::cast(it->objectId().safeOpenObject()); if ( br.isNull() ) continue; OdDbDynBlockReference ref( br.get() ); if ( !ref.isDynamicBlock() ) continue; OdDbDynBlockReferencePropertyArray a; ref.getBlockProperties(a); int N = printProperties( a, pDbCmdCtx ); if ( N == 0 ) continue; int n = pDbCmdCtx->userIO()->getInt( OD_T("Select property:"), OdEd::kInpDefault, 1 ); if ( n > N || n < 1 ) return; modifyProperty( n, a, pDbCmdCtx ); } } void ResetBlockCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select( OD_T("Select dynamic block reference to reset:") ); for ( OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next() ) { OdDbBlockReferencePtr br = OdDbBlockReference::cast(it->objectId().safeOpenObject()); if ( br.isNull() ) continue; OdDbDynBlockReference ref( br.get() ); if ( !ref.isDynamicBlock() ) continue; ref.resetBlock(); } } void ConvertToStaticBlockCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select( OD_T("Select dynamic block reference to convert:") ); for ( OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next() ) { OdDbBlockReferencePtr br = OdDbBlockReference::cast(it->objectId().safeOpenObject()); if ( br.isNull() ) continue; OdDbDynBlockReference ref( br.get() ); if ( !ref.isDynamicBlock() ) continue; ref.convertToStaticBlock( pIO->getString( OD_T("Enter new block name:"), OdEd::kGstAllowSpaces, OD_T("*U") ) ); } } void PrintDynamicBlockNamesCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbDatabasePtr db = pDbCmdCtx->database(); OdDbBlockTablePtr blockTablePtr = db->getBlockTableId().safeOpenObject(); for (OdDbSymbolTableIteratorPtr itPtr = blockTablePtr->newIterator(); !itPtr->done(); itPtr->step()) { OdDbBlockTableRecordPtr recordPtr = itPtr->getRecord(OdDb::kForRead); if (!recordPtr.isNull()) { if (recordPtr->isAnonymous() && OdDbDynBlockReference::isDynamicBlock(recordPtr->objectId())) { OdResBufPtr pXData = recordPtr->xData(AcDbDynamicBlockTrueName); pXData = pXData->next(); OdString recordName = pXData->getString(); pDbCmdCtx->dbUserIO()->putString(recordName); } } } } // prints dynamic block evaluation graph in DOT format // see http://www.graphviz.org/ for details void PrintGraphCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select(L"Select dynamic block reference to print graph:"); for ( OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next() ) { OdDbBlockReferencePtr br = OdDbBlockReference::cast(it->objectId().safeOpenObject()); if ( br.isNull() ) continue; OdDbDynBlockReference dr( br.get() ); if ( !dr.isDynamicBlock() ) continue; OdDbEvalGraphPtr gr = OdDbEvalGraph::getGraph(dr.dynamicBlockTableRecord().safeOpenObject(), ACAD_ENHANCEDBLOCK); if (gr.isNull()) continue; OdString s; s.format(L"digraph G%" PRIX64W L" {", (OdUInt64)gr->objectId().getHandle()); pIO->putString(s); OdDbEvalNodeIdArray allNodes; gr->getAllNodes(allNodes); for (unsigned int i = 0; i < allNodes.size(); ++i) { int N = allNodes[i]; OdDbEvalExprPtr e = gr->getNode(N); if (e.isNull()) continue; // print node declaration if (e->isKindOf(OdDbBlockElement::desc())) { s.format(L" Node%d [label=\"%ls (%" PRIX64W L")\\nname = %ls, (%d)\"];", N, e->isA()->name().c_str(), (OdUInt64)e->objectId().getHandle(), OdDbBlockElementPtr(e)->name().c_str(), (int)e->nodeId()); } else if (e->isKindOf(OdDbBlockGripExpr::desc())) { s.format(L" Node%d [shape=box,label=\"(%" PRIX64W L"), (%d)\"];", N, (OdUInt64)e->objectId().getHandle(), (int)e->nodeId()); } else { s.format(L" Node%d [label=\"%ls (%" PRIX64W L"), (%d)\"];", N, e->isA()->name().c_str(), (OdUInt64)e->objectId().getHandle(), (int)e->nodeId()); } pIO->putString(s); OdDbEvalEdgeInfoArray edges; gr->getOutgoingEdges(N, edges); for (unsigned int j = 0; j < edges.size(); ++j) { if (edges[j].isInvertible() && edges[j].isSuppressed()) { s.format(L" Node%d -> Node%d [label=\"I & S\"];", N, edges[j].to()); } else if (edges[j].isInvertible()) { s.format(L" Node%d -> Node%d [label=\"I\"];", N, edges[j].to()); } else if (edges[j].isSuppressed()) { s.format(L" Node%d -> Node%d [label=\"S\"];", N, edges[j].to()); } else { s.format(L" Node%d -> Node%d;", N, edges[j].to()); } pIO->putString(s); } // end edges if (e->isKindOf(OdDbBlockAction::desc())) { OdDbObjectIdArray selection = ((OdDbBlockAction*)e.get())->elementSelectionSet(); for (unsigned int j = 0; j < selection.size(); ++j) { OdDbEvalExprPtr se = selection[j].openObject(); if (!se.isNull()) { s.format(L" Node%d -> Node%d [style=dotted, color=red];", N, se->nodeId()); pIO->putString(s); } } } if (auto c = OdDbEvalConnectable::cast(e)) { OdStringArray names; c->getConnectionNames(names); for (auto name : names) { OdDbEvalNodeIdArray ia; c->getConnectedObjects(name, ia); for (auto id : ia) { s.format(L" Node%d -> Node%d [style=dashed, color=green];", N, id); pIO->putString(s); } } } } // end graph pIO->putString(L"}"); } } void UpdateAnonymousBlocksCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select(OD_T("Select dynamic block reference:")); for (OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next()) { { OdDbObjectPtr pObj = it->objectId().openObject(); if (!pObj->isKindOf(OdDbBlockReference::desc())) continue; } OdDbDynBlockReference dbr(it->objectId()); if (!dbr.isDynamicBlock()) continue; OdDbDynBlockTableRecord dbtr(dbr.dynamicBlockTableRecord()); dbtr.updateAnonymousBlocks(); } } void CreateHistoryRecordCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIO* pIO = pDbCmdCtx->dbUserIO(); OdDbSelectionSetPtr pSSet = pIO->select(OD_T("Select dynamic block reference:")); for (OdDbSelectionSetIteratorPtr it = pSSet->newIterator(); !it->done(); it->next()) { if (auto br = OdDbBlockReference::cast(it->objectId().openObject())) { if (auto xd = OdDbDictionary::cast(br->extensionDictionary().openObject())) { if (auto rep = OdDbDictionary::cast(xd->getAt(AcDbBlockRepresentation).openObject())) { OdDbDictionaryPtr dic2 = rep->getAt(AppDataCache, OdDb::kForWrite); if (dic2->getAt(ACAD_ENHANCEDBLOCKHDATA)) continue; auto blockDataId = dic2->getAt(ACAD_ENHANCEDBLOCKDATA); if (!blockDataId) continue; OdDbDictionaryPtr blockData = blockDataId.safeOpenObject(); auto blockHData = OdDbDictionary::createObject(); auto blockHDataId = dic2->setAt(ACAD_ENHANCEDBLOCKHDATA, blockHData); OdDbObjectIdArray ids; for (auto e : *blockData) ids.append(e->objectId()); auto im = OdDbIdMapping::createObject(); pDbCmdCtx->database()->deepCloneObjects(ids, blockHDataId, *im); auto history = OdDbXrecord::createObject(); history->setFromRbChain(OdResBuf::newRb(1070, OdInt16(0))); dic2->setAt(ACAD_ENHANCEDBLOCKHISTORY, history); } } } } }