void AsdkWblockReactor::otherWblock(AcDbDatabase* pDestDb, AcDbIdMapping& idMap, AcDbDatabase* pSrcDb) { // To find the destination Model Space, you must look // it up in the ID map: AcDbBlockTable *pSrcBlockTable; pSrcDb->getSymbolTable(pSrcBlockTable, AcDb::kForRead); AcDbObjectId srcModelSpaceId; pSrcBlockTable->getAt(ACDB_MODEL_SPACE, srcModelSpaceId); pSrcBlockTable->close(); AcDbIdPair idPair; idPair.setKey(srcModelSpaceId); idMap.compute(idPair); AcDbBlockTableRecord *pDestBTR; acdbOpenAcDbObject((AcDbObject*&)pDestBTR, idPair.value(), AcDb::kForRead, Adesk::kTrue); // END CODE APPEARING IN SDK DOCUMENT. acutPrintf("\nCorrect destination BTR's ObjectId is:\t\t%Ld", pDestBTR->objectId().asOldId()); pDestBTR->close(); // Incorrect way done here so that the wrong value can be // compared to the correct value // AcDbBlockTable *pDestBlockTable; pDestDb->getSymbolTable(pDestBlockTable, AcDb::kForRead); pDestBlockTable->getAt(ACDB_MODEL_SPACE, pDestBTR, AcDb::kForRead); pDestBlockTable->close(); acutPrintf("\nIncorrect destination BTR's ObjectId is \t\t%Ld", pDestBTR->objectId().asOldId()); pDestBTR->close(); // source database Model Space BTR's ObjectId is shown to // demonstrate that this is what the incorrect method gets // pSrcDb->getSymbolTable(pSrcBlockTable, AcDb::kForRead); pSrcBlockTable->getAt(ACDB_MODEL_SPACE, srcModelSpaceId); pSrcBlockTable->close(); acutPrintf("\nSource Database's Model Space BTR's ObjectId is \t%Ld", srcModelSpaceId.asOldId()); }
Acad::ErrorStatus ArxDbgDbEntity::wblockClone(AcRxObject* pOwner, AcDbObject*& pClone, AcDbIdMapping& idMap, Adesk::Boolean isPrimary) const { if (ArxDbgOptions::m_instance.m_showWblockCloneDetails) { CString titleStr, tmpStr; titleStr.Format(_T("Beginning -- wblockClone: %s"), ArxDbgUtils::objToClassAndHandleStr(const_cast<ArxDbgDbEntity*>(this), tmpStr)); ArxDbgUiTdmIdMap dbox(&idMap, acedGetAcadDwgView(), titleStr); dbox.DoModal(); } AcDb::DeepCloneType type = idMap.deepCloneContext(); // if xrefInsert or xrefBind, we know everything will // be cloned, so just let normal routine handle this if ((type == AcDb::kDcXrefBind) || (type == AcDb::kDcXrefInsert)) { return AcDbEntity::wblockClone(pOwner, pClone, idMap, isPrimary); } // if we've already been cloned, just return AcDbIdPair idPair(objectId(), AcDbObjectId::kNull, true); if (idMap.compute(idPair) && (idPair.value() != AcDbObjectId::kNull)) { return Acad::eOk; } // If isPrimary is kTrue, then override the default cloning // within our own cloning, which would set it to kFalse, // by cloning our referenced entity first. if (isPrimary) { // now ask derived classes what references they want cloned for them AcDbObjectIdArray refEntIds; AcDbIntArray refTypes; getCloneReferences(type, refEntIds, refTypes); ASSERT(refEntIds.length() == refTypes.length()); // clone each entity we reference first and change the value // of isPrimary to fake it out. Since we clone these first, // when the normal wblockClone is called, it will see that // they are already in the set of cloned objects and will not // try to clone it again. AcDbEntity* ent; Acad::ErrorStatus es; int len = refEntIds.length(); for (int i=0; i<len; i++) { if (refTypes[i] == kClone) { es = acdbOpenAcDbEntity(ent, refEntIds[i], AcDb::kForRead); if (es == Acad::eOk) { // this method only works if they come from the same block // (which SHOULD always be the case!) if (blockId() == ent->blockId()) { // Use the same owner, and pass in the same isPrimary value AcDbObject* pSubClone = NULL; es = ent->wblockClone(pOwner, pSubClone, idMap, Adesk::kTrue); if (pSubClone != NULL) pSubClone->close(); if (es != Acad::eOk) { ASSERT(0); } } else { ASSERT(0); } ent->close(); } } } } // Now we can clone ourselves via calling our parent's method. Acad::ErrorStatus es = AcDbEntity::wblockClone(pOwner, pClone, idMap, isPrimary); if (ArxDbgOptions::m_instance.m_showWblockCloneDetails) { CString titleStr, tmpStr; titleStr.Format(_T("End -- wblockClone: %s"), ArxDbgUtils::objToClassAndHandleStr(const_cast<ArxDbgDbEntity*>(this), tmpStr)); ArxDbgUiTdmIdMap dbox(&idMap, acedGetAcadDwgView(), titleStr); dbox.DoModal(); } return es; }
Acad::ErrorStatus ArxDbgDbEntity::deepClone(AcDbObject* pOwner, AcDbObject*& pClonedObject, AcDbIdMapping& idMap, Adesk::Boolean isPrimary) const { // You should always pass back pClonedObject == NULL // if, for any reason, you do not actually clone it // during this call. The caller should pass it in // as NULL, but to be safe, we set it here as well. pClonedObject = NULL; if (ArxDbgOptions::m_instance.m_showDeepCloneDetails) { CString titleStr, tmpStr; titleStr.Format(_T("Beginning -- deepClone: %s"), ArxDbgUtils::objToClassAndHandleStr(const_cast<ArxDbgDbEntity*>(this), tmpStr)); ArxDbgUiTdmIdMap dbox(&idMap, acedGetAcadDwgView(), titleStr); dbox.DoModal(); } AcDb::DeepCloneType type = idMap.deepCloneContext(); // if we know everything will be cloned for us, just let // the base class do everything for us. if ((type == AcDb::kDcInsert) || (type == AcDb::kDcInsertCopy) || (type == AcDb::kDcExplode)) return AcDbEntity::deepClone(pOwner, pClonedObject, idMap, isPrimary); // following case happens when doing a AcDbDatabase::deepCloneObjects() // and the owner happens to be the same... then its really like a // kDcCopy, otherwise deepCloneObjects() is like a kDcBlock if (type == AcDb::kDcObjects) { if (ownerId() == pOwner->objectId()) type = AcDb::kDcCopy; else type = AcDb::kDcBlock; } // now ask derived classes what references they want cloned for them AcDbObjectIdArray refEntIds; AcDbIntArray refTypes; getCloneReferences(type, refEntIds, refTypes); ASSERT(refEntIds.length() == refTypes.length()); // if derived class doesn't have any references to take care of, then // we will just let the AcDbEntity::deepClone() take care of things. if (refEntIds.isEmpty()) return AcDbEntity::deepClone(pOwner, pClonedObject, idMap, isPrimary); // If this object is in the idMap and is already // cloned, then return. bool tmpIsPrimary = isPrimary ? true : false; // get around compiler performance warning AcDbIdPair idPair(objectId(), AcDbObjectId::kNull, false, tmpIsPrimary); if (idMap.compute(idPair) && (idPair.value() != NULL)) return Acad::eOk; // STEP 1: // Create the clone // AcDbObject *pClone = (AcDbObject*)isA()->create(); if (pClone != NULL) pClonedObject = pClone; // set the return value else return Acad::eOutOfMemory; // STEP 2: // Append the clone to its new owner. In this example, // we know that we are derived from AcDbEntity, so we // can expect our owner to be an AcDbBlockTableRecord, // unless we have set up an ownership relationship with // another of our objects. In that case, we need to // establish how we connect to that owner in our own // way. This sample shows a generic method using // setOwnerId(). // AcDbBlockTableRecord *pBTR = AcDbBlockTableRecord::cast(pOwner); if (pBTR != NULL) { AcDbEntity* ent = AcDbEntity::cast(pClone); pBTR->appendAcDbEntity(ent); } else { if (isPrimary) return Acad::eInvalidOwnerObject; // Some form of this code is only necessary if // anyone has set up an ownership for our object // other than with an AcDbBlockTableRecord. // pOwner->database()->addAcDbObject(pClone); pClone->setOwnerId(pOwner->objectId()); } // STEP 3: // Now we copy our contents to the clone. This is done // using an AcDbDeepCloneFiler. This filer keeps a // list of all AcDbHardOwnershipIds and // AcDbSoftOwnershipIds we, and any classes we derive // from, have. This list is then used to know what // additional, "owned" objects need to be cloned below. // AcDbDeepCloneFiler filer; dwgOut(&filer); // STEP 4: // Rewind the filer and read the data into the clone. // filer.seek(0L, AcDb::kSeekFromStart); pClone->dwgIn(&filer); // STEP 5: // This must be called for all newly created objects // in deepClone. It is turned off by endDeepClone() // after it has translated the references to their // new values. // pClone->setAcDbObjectIdsInFlux(); // STEP 6: // Add the new information to the idMap. We can use // the idPair started above. // idPair.setValue(pClonedObject->objectId()); idPair.setIsCloned(Adesk::kTrue); idMap.assign(idPair); // STEP 7: // Using the filer list created above, find and clone // any owned objects. // AcDbObject *pSubObject; AcDbObject *pClonedSubObject; AcDbObjectId id; Acad::ErrorStatus es; while (filer.getNextOwnedObject(id)) { // Open the object and clone it. Note that we now // set "isPrimary" to kFalse here because the object // is being cloned, not as part of the primary set, // but because it is owned by something in the // primary set. es = acdbOpenAcDbObject(pSubObject, id, AcDb::kForRead); if (es != Acad::eOk) continue; // could have been NULL or erased pClonedSubObject = NULL; pSubObject->deepClone(pClonedObject, pClonedSubObject, idMap, Adesk::kFalse); // If this is a kDcInsert context, the objects // may be "cheapCloned". In this case, they are // "moved" instead of cloned. The result is that // pSubObject and pClonedSubObject will point to // the same object. So, we only want to close // pSubObject if it really is a different object // than its clone. if (pSubObject != pClonedSubObject) pSubObject->close(); // The pSubObject may either already have been // cloned, or for some reason has chosen not to be // cloned. In that case, the returned pointer will // be NULL. Otherwise, since we have no immediate // use for it now, we can close the clone. if (pClonedSubObject != NULL) pClonedSubObject->close(); } // clone the referenced entities AcDbObject* ent; int len = refEntIds.length(); for (int i=0; i<len; i++) { if (refTypes[i] == kClone) { es = acdbOpenAcDbObject(ent, refEntIds[i], AcDb::kForRead); if (es == Acad::eOk) { pClonedSubObject = NULL; es = ent->deepClone(pOwner, pClonedSubObject, idMap, Adesk::kTrue); if (es == Acad::eOk) { // see comment above about cheap clone if (ent != pClonedSubObject) ent->close(); if (pClonedSubObject != NULL) pClonedSubObject->close(); } } } // this case is needed for RefEdit so we can pass its validation // test when editing a blockReference. We don't actually clone it // but we add it to the map so it thinks it got cloned and is therefore // a valid "Closed Set" of objects. else if (refTypes[i] == kFakeClone) { AcDbIdPair idPair(refEntIds[i], refEntIds[i], false, false, true); idMap.assign(idPair); } } if (ArxDbgOptions::m_instance.m_showDeepCloneDetails) { CString titleStr, tmpStr; titleStr.Format(_T("End -- deepClone: %s"), ArxDbgUtils::objToClassAndHandleStr(const_cast<ArxDbgDbEntity*>(this), tmpStr)); ArxDbgUiTdmIdMap dbox(&idMap, acedGetAcadDwgView(), titleStr); dbox.DoModal(); } // Leave pClonedObject open for the caller return Acad::eOk; }