void ArxDbgAppEditorReactor::endDeepClone(AcDbIdMapping& idMap) { if ((idMap.deepCloneContext() == AcDb::kDcBlock) || (idMap.deepCloneContext() == AcDb::kDcObjects)) { verifyClonedReferences(idMap); } }
void ArxDbgEditorReactor::beginDeepClone(AcDbDatabase* pTo, AcDbIdMapping& idMap) { CString str, str2; ArxDbgUtils::deepCloneContextToStr(idMap.deepCloneContext(), str2); str.Format(_T("Begin Deep Clone: context = %s"), static_cast<LPCTSTR>(str2)); printReactorMessage(str, pTo); if (m_showDetails) { ArxDbgUiTdmIdMap dbox(&idMap, acedGetAcadDwgView(), _T("Begin Deep Clone")); dbox.DoModal(); } }
void ArxDbgAppEditorReactor::beginDeepCloneXlation( AcDbIdMapping& idMap, Acad::ErrorStatus* es) { m_didTheseDicts.setLogicalLength(0); // reset the dictionaries we have processed AcDbDatabase* origDb; AcDbDatabase* destDb; idMap.origDb(origDb); idMap.destDb(destDb); AcDbObject* clonedObj; AcDbObject* objToClone; // we catch this event so that we can wblock objects that are not entities. // This happens from the class ArxDbgUiTdcWblockClone where you are allowed // to pick non-entities to wblock to a new/existing drawing. AcDbDatabase::wblock() // only allows entities to be in the set of objects passed to it, but you // can manually wblockClone them yourself here. if (idMap.deepCloneContext() == AcDb::kDcWblock) { // see what non-entity objects we have to clone by hand. AcDbObjectIdArray handCloneObjects; m_cloneSet.getObjectsForDatabase(origDb, handCloneObjects); if (handCloneObjects.isEmpty()) return; // walk through the clone set and try to clone // everything. If something is already cloned, // its ok, it won't be cloned again or return an // error, it will just set clonedObj to NULL. CString str; int len = handCloneObjects.length(); for (int i=0; i<len; i++) { clonedObj = NULL; if (acdbOpenAcDbObject(objToClone, handCloneObjects[i], AcDb::kForRead) == Acad::eOk) { objToClone->wblockClone(destDb, clonedObj, idMap, Adesk::kFalse); if (clonedObj != NULL) { acutPrintf(_T("\nArxDbgAppEditorReactor: cloned additional object [%s, %s]"), ArxDbgUtils::objToClassStr(clonedObj), ArxDbgUtils::objToHandleStr(objToClone, str)); clonedObj->close(); } objToClone->close(); } } } // catching this event allows us to correctly bring in our Dictionary Records. // If we don't do this, then they will be orphaned. AutoCAD will not hook up // the cloned dictionary records automatically. else if ((ArxDbgOptions::m_instance.m_doDictRecordInsertByHand) && ((idMap.deepCloneContext() == AcDb::kDcInsert) || (idMap.deepCloneContext() == AcDb::kDcInsertCopy))) { // have to manually find all things we are interested in inserting. // So, look for all ArxDbgDictRecords, if its a dict record, // make sure owner dict is cloned in new database AcDbObjectIdArray objIds; collectAllDictRecords(origDb, objIds); int len = objIds.length(); for (int i=0; i<len; i++) { // find out if object is a dict record Acad::ErrorStatus es; es = acdbOpenObject(objToClone, objIds[i], AcDb::kForRead); if (es == Acad::eOk) { acutPrintf("\nArxDbgAppEditorReactor: hand inserting [%s]", ArxDbgUtils::objToClassStr(objToClone)); // clone the owner dictionary if one of our dictionary records if (objToClone->isKindOf(ArxDbgDbDictRecord::desc())) insertCloneOwnerDict(objToClone->ownerId(), destDb, idMap); objToClone->close(); } } } }
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; }
// AsdkNODEdReactor is derived from AcEditorReactor // void AsdkNODEdReactor::beginDeepCloneXlation( AcDbIdMapping& idMap, Acad::ErrorStatus* pRetStat) { Acad::ErrorStatus es; AcDbObjectId dictId; if ( idMap.deepCloneContext() != AcDb::kDcWblock && idMap.deepCloneContext() != AcDb::kDcInsert) return; // Get the "from" and "to" databases. // AcDbDatabase *pFrom, *pTo; idMap.origDb(pFrom); idMap.destDb(pTo); // See if the "from" database has our dictionary, and // open it. If it doesn't have one, we are done. // AcDbDictionary *pSrcNamedObjDict; pFrom->getNamedObjectsDictionary(pSrcNamedObjDict, AcDb::kForRead); es = pSrcNamedObjDict->getAt(kpDictionary, dictId); pSrcNamedObjDict->close(); if (es == Acad::eKeyNotFound) return; AcDbDictionary *pSrcDict; acdbOpenObject(pSrcDict, dictId, AcDb::kForRead); AcDbObject *pClone; switch (idMap.deepCloneContext()) { case AcDb::kDcWblock: // WBLOCK clones all, or part of a drawing into a // newly created drawing. This means that the // NamedObject Dictionary is always cloned, and // its AcDbObjectIds are in flux. Therefore, you // cannot use getAt() or setAt() on the dictionary // in the new database. This is because the // cloned dictionary references all refer to the // original objects. During Deep Clone translation, // all cloned entries will be translated to the // new objects, and entries not cloned will be // "removed" by getting "translated" to NULL. // // The cloning of entries in our own dictionary are // not handled here. If all are to be cloned, then // call setTreatElementsAsHard(Adesk::kTrue) on the // dictionary. Otherwise, only those entries which // are refered to by hard references in other // wblocked objects, will have been cloned via // those references. // In this example, we will always write out all of // the records. Since TreatElementsAsHard is not // currently persistent, we reset it here each time. // pSrcDict->upgradeOpen(); pSrcDict->setTreatElementsAsHard(Adesk::kTrue); pClone = NULL; pSrcDict->wblockClone(pTo, pClone, idMap, Adesk::kFalse); if (pClone != NULL) pClone->close(); break; case AcDb::kDcInsert: // In INSERT, an entire drawing is cloned, and // "merged" into a pre-existing drawing. This // means that the destination drawing may already // have our dictionary - in which case we have to // merge our entries into the destination // dictionary. So, first we must find out if // the destination NamedObjects dictionary has // our dictionary. // AcDbDictionary *pDestNamedDict; pTo->getNamedObjectsDictionary(pDestNamedDict, AcDb::kForWrite); // Since INSERT does not clone the destination // NamedObjects dictionary, we can use getAt() // on it. // es = pDestNamedDict->getAt(kpDictionary, dictId); // If our dictionary does not yet exist in the // NamedObjects dictionary, which is not itself // cloned, we have to both clone and add our // dictionary to it. Since dictionary entries are // ownership references, all of our entries will // also be cloned at this point, so we are done. // if (es == Acad::eKeyNotFound) { pClone = NULL; pSrcDict->deepClone(pDestNamedDict, pClone, idMap); // Unless we have overridden the deepClone // of our dictionary, we should expect it to // always be cloned here. // if (pClone == NULL) { *pRetStat = Acad::eNullObjectId; break; } pDestNamedDict->setAt(kpDictionary, pClone, dictId); pDestNamedDict->close(); pClone->close(); break; } pDestNamedDict->close(); // Our dictionary already exists in the destination // database, so now we must "merge" the entries // into it. Since we have not cloned our // destination dictionary, its objectIds are not in // flux, and we can use getAt() and setAt() on it. // AcDbDictionary *pDestDict; acdbOpenObject(pDestDict, dictId, AcDb::kForWrite); AcDbObject *pObj, *pObjClone; AcDbDictionaryIterator* pIter; pIter = pSrcDict->newIterator(); for (; !pIter->done(); pIter->next()) { const char *pName = pIter->name(); pIter->getObject(pObj, AcDb::kForRead); // If the dictionary contains any references // and/or other objects have references to it, // you must either use deepClone() or put the // idPairs into the idMap here, so that they // will be in the map for translation. // pObjClone = NULL; pObj->deepClone(pDestDict, pObjClone, idMap); // INSERT usually uses a method of cloning // called CheapClone, where it "moves" objects // into the destination database instead of // actually cloning them. When this happens, // pObj and pObjClone are pointers to the // same object. We only want to close pObj // here if it really is a different object. // if (pObj != pObjClone) pObj->close(); if (pObjClone == NULL) continue; // If the name already exists in our // destination dictionary, it must be changed // to something unique. In this example, the // name is changed to an annonymous entry. // The setAt() method will automatically append // a unique identifier to each name beginning // with "*". It will become something like, // "*S04". // if ( pDestDict->getAt(pName, dictId) == Acad::eKeyNotFound) pDestDict->setAt(pName, pObjClone, dictId); else pDestDict->setAt("*S", pObjClone, dictId); pObjClone->close(); } delete pIter; pDestDict->close(); break; default: break; } pSrcDict->close(); }
// AsdkEdReactor is derived from AcEditorReactor // void AsdkEdReactor::beginDeepCloneXlation(AcDbIdMapping& idMap, Acad::ErrorStatus* es) { if (idMap.deepCloneContext() == AcDb::kDcWblock && getYorN(_T("Wblock all Text Styles"))) { AcDbDatabase *pOrigDb, *pDestDb; if (idMap.origDb(pOrigDb) != Acad::eOk) return; *es = idMap.destDb(pDestDb); if (*es != Acad::eOk) return; AcDbTextStyleTable *pTsTable; *es = pOrigDb->getSymbolTable(pTsTable, AcDb::kForRead); if (*es != Acad::eOk) return; AcDbTextStyleTableIterator *pTsIter; *es = pTsTable->newIterator(pTsIter); if (*es != Acad::eOk) { pTsTable->close(); return; } AcDbTextStyleTableRecord *pTsRecord; AcDbObject *pClonedObj; for (; !pTsIter->done(); pTsIter->step()) { *es = pTsIter->getRecord(pTsRecord, AcDb::kForRead); if (*es != Acad::eOk) { delete pTsIter; pTsTable->close(); return; } // We don't need to check for already cloned // Records. If the Text Style is already // cloned, wblockClone will return Acad::eOk // and pCloneObj will be NULL. // pClonedObj = NULL; *es = pTsRecord->wblockClone(pDestDb, pClonedObj, idMap, Adesk::kFalse); if (*es != Acad::eOk) { pTsRecord->close(); delete pTsIter; pTsTable->close(); return; } *es = pTsRecord->close(); if (*es != Acad::eOk) { delete pTsIter; pTsTable->close(); return; } if (pClonedObj != NULL) { *es = pClonedObj->close(); if (*es != Acad::eOk) { delete pTsIter; pTsTable->close(); return; } } } delete pTsIter; *es = pTsTable->close(); } }