The following example shows how you can use reactors to establish dependencies among database objects. In this example, when you change one line, the other line changes.
class AsdkObjectToNotify : public AcDbObject // // AsdkObjectToNotify - customized AcDbObject for persistent // reactor to notify. // { public: ACRX_DECLARE_MEMBERS(AsdkObjectToNotify); AsdkObjectToNotify() {}; void eLinkage(AcDbObjectId i, double f=1.0) {mId=i; mFactor=f; }; void modified(const AcDbObject*); Acad::ErrorStatus dwgInFields(AcDbDwgFiler*); Acad::ErrorStatus dwgOutFields(AcDbDwgFiler*) const; Acad::ErrorStatus dxfInFields(AcDbDxfFiler*); Acad::ErrorStatus dxfOutFields(AcDbDxfFiler*) const; private: AcDbObjectId mId; double mFactor; }; ACRX_DXF_DEFINE_MEMBERS(AsdkObjectToNotify, AcDbObject, AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent, 0, ASDKOBJECTTONOTIFY, persreac); // This function is called every time the line it's // "watching" is modified. When it's called, it opens the // other line of the pair and changes that line's length to // match the new length of the line that's just been // modified. // void AsdkObjectToNotify::modified(const AcDbObject* pObj) { AcDbLine *pLine = AcDbLine::cast(pObj); if (!pLine) { const char* cstr = pObj->isA()->name(); acutPrintf("This is a %s.\n", cstr); acutPrintf("I only work with lines. Sorry.\n"); return; } acutPrintf("\nReactor attached to %lx calling %lx.\n", pLine->objectId(), mId); // This open fails during notification caused by a // reactor being added to the entity or when this // notification is in reaction to a change due to the // other line's reactor changing this line. This // properly prevents an infinite recursive loop // between the two lines and their reactors. // AcDbLine *pLine2; if (acdbOpenObject((AcDbObject*&)pLine2, mId, AcDb::kForWrite) == Acad::eOk) { // Gets length of line entity we're being notified // has just been modified. // AcGePoint3d p = pLine->startPoint(); AcGePoint3d q = pLine->endPoint(); AcGeVector3d v = q-p; double len = v.length(); // Updates other entity to match. // p = pLine2->startPoint(); q = pLine2->endPoint(); v = q-p; v = len * mFactor * v.normal(); pLine2->setEndPoint(p+v); pLine2->close(); } } // Files an object's information in. // Acad::ErrorStatus AsdkObjectToNotify::dwgInFields(AcDbDwgFiler* filer) { assertWriteEnabled(); AcDbObject::dwgInFields(filer); filer->readItem(&mFactor); filer->readItem((AcDbSoftPointerId*) &mId); return filer->filerStatus(); } // Files an object's information out. // Acad::ErrorStatus AsdkObjectToNotify::dwgOutFields(AcDbDwgFiler* filer) const { assertReadEnabled(); AcDbObject::dwgOutFields(filer); filer->writeItem(mFactor); filer->writeItem((AcDbSoftPointerId&)mId); return filer->filerStatus(); } // Files an object's information in from DXF and AutoLISP. // Acad::ErrorStatus AsdkObjectToNotify::dxfInFields(AcDbDxfFiler* filer) { assertWriteEnabled(); Acad::ErrorStatus es; if ((es = AcDbObject::dxfInFields(filer)) != Acad::eOk) { return es; } // Checks if we're at the right subclass data marker. // if(!filer->atSubclassData("AsdkObjectToNotify")) { return Acad::eBadDxfSequence; } struct resbuf rbIn; while (es == Acad::eOk) { if ((es = filer->readItem(&rbIn)) == Acad::eOk) { if (rbIn.restype == AcDb::kDxfReal) { mFactor = rbIn.resval.rreal; } else if (rbIn.restype == AcDb::kDxfSoftPointerId) { // ObjectIds are filed in as ads_names. // acdbGetObjectId(mId, rbIn.resval.rlname); } else { // invalid group return(filer->pushBackItem()); } } } return filer->filerStatus(); } // Files an object's information out to DXF and AutoLISP. // Acad::ErrorStatus AsdkObjectToNotify::dxfOutFields(AcDbDxfFiler* filer) const { assertReadEnabled(); AcDbObject::dxfOutFields(filer); filer->writeItem(AcDb::kDxfSubclass, "AsdkObjectToNotify"); filer->writeItem(AcDb::kDxfReal, mFactor); filer->writeItem(AcDb::kDxfSoftPointerId, mId); return filer->filerStatus(); } // Creates two lines and two AsdkObjectToNotify objects and // ties them all together. // void assocLines() { AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase(); AcDbObjectId aId, bId; AcDbLine *pLineA = new AcDbLine; pLineA->setDatabaseDefaults(pDb); pLineA->setStartPoint(AcGePoint3d(1, 1, 0)); pLineA->setEndPoint(AcGePoint3d(2, 1, 0)); addToModelSpace(aId, pLineA); acutPrintf( "Line A is %lx from 1,1 to 2,1.\n", pLineA->objectId()); AcDbLine *pLineB = new AcDbLine; pLineB->setDatabaseDefaults(pDb); pLineB->setStartPoint(AcGePoint3d(1, 2, 0)); pLineB->setEndPoint(AcGePoint3d(2, 2, 0)); addToModelSpace(bId, pLineB); acutPrintf("Line B is %lx from 1,2 to 2,2.\n", pLineB->objectId()); // Opens the named object dictionary, and checks if there is // an entry with the key "ASDK_DICT". If not, creates a // dictionary and add it. // AcDbDictionary *pNamedObj; AcDbDictionary *pNameList; pDb->getNamedObjectsDictionary(pNamedObj, AcDb::kForWrite); if (pNamedObj->getAt("ASDK_DICT", (AcDbObject*&)pNameList, AcDb::kForWrite) == Acad::eKeyNotFound) { pNameList = new AcDbDictionary; AcDbObjectId DictId; pNamedObj->setAt("ASDK_DICT", pNameList, DictId); } pNamedObj->close(); // Creates the AsdkObjectToNotify for line A. // AsdkObjectToNotify *pObj = new AsdkObjectToNotify(); pObj->eLinkage(bId); AcDbObjectId objId; if ((pNameList->getAt("object_to_notify_A", objId)) == Acad::eKeyNotFound) { pNameList->setAt("object_to_notify_A", pObj, objId); pObj->close(); } else { delete pObj; acutPrintf("object_to_notify_A already exists\n"); } // Sets up persistent reactor link between line A // and AsdkObjectToNotify. // pLineA->addPersistentReactor(objId); pLineA->close(); // Creates the AsdkObjectToNotify for line B. // pObj = new AsdkObjectToNotify(); pObj->eLinkage(aId); if ((pNameList->getAt("object_to_notify_B", objId)) == Acad::eKeyNotFound) { pNameList->setAt("object_to_notify_B", pObj, objId); pObj->close(); } else { delete pObj; acutPrintf("object_to_notify_B already exists\n"); } pNameList->close(); // Sets up persistent reactor link between line B // and AsdkObjectToNotify. // pLineB->addPersistentReactor(objId); pLineB->close(); } // Adds an entity to model space, but does not close // the entity. // void addToModelSpace(AcDbObjectId &objId, AcDbEntity* pEntity) { AcDbBlockTable *pBlockTable; AcDbBlockTableRecord *pSpaceRecord; acdbHostApplicationServices()->workingDatabase() ->getSymbolTable(pBlockTable, AcDb::kForRead); pBlockTable->getAt(ACDB_MODEL_SPACE, pSpaceRecord, AcDb::kForWrite); pBlockTable->close(); pSpaceRecord->appendAcDbEntity(objId, pEntity); pSpaceRecord->close(); return; } // This is the initialization function called from acrxEntryPoint() // during the kInitAppMsg case. This function is used to add // commands to the command stack. // void initApp() { acedRegCmds->addCommand("ASDK_ALINES", "ASDK_ALINES", "ALINES", ACRX_CMD_MODAL, assocLines); AsdkObjectToNotify::rxInit(); acrxBuildClassHierarchy(); } // This is the clean-up function called from acrxEntryPoint() during // the kUnloadAppMsg case. This function removes this application's // command set from the command stack. // void unloadApp() { acedRegCmds->removeGroup("ASDK_ALINES"); // Removes the AsdkObjectToNotify class from the ACRX // runtime class hierarchy. If this is done while the // database is still active, it should cause all objects // of class AsdkObjectToNotify to be turned into proxies. // deleteAcRxClass(AsdkObjectToNotify::desc()); } // ObjectARX entry point // extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* appId) { switch (msg) { case AcRx::kInitAppMsg: acrxDynamicLinker->unlockApplication(appId); acrxDynamicLinker->registerAppMDIAware(appId); initApp(); break; case AcRx::kUnloadAppMsg: unloadApp(); } return AcRx::kRetOK; }