void ArxDbgAppEditorReactor::insertCloneMergeDicts(AcDbDictionary* srcDict, AcDbDictionary* destDict, AcDbIdMapping& idMap) { AcDbObjectId tmpObjId; AcDbObject* objToClone; AcDbObject* clonedObj; AcDbDictionaryIterator* iter; iter = srcDict->newIterator(); for (; !iter->done(); iter->next()) { iter->getObject(objToClone, AcDb::kForRead); // if an entry with this name is found already, don't clone // it, add to the idMap to say that this one got translated // to one that already exists const char *pName = iter->name(); if (destDict->getAt(pName, tmpObjId) == Acad::eOk) { AcDbIdPair idPair(objToClone->objectId(), tmpObjId, true, false, true); idMap.assign(idPair); objToClone->close(); } else { // doesn't exist yet in the destination dict, so clone and // place it there. clonedObj = NULL; objToClone->deepClone(destDict, clonedObj, 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, // objToClone and clonedObj are pointers to the // same object. We only want to close pObj // here if it really is a different object. if (objToClone != clonedObj) objToClone->close(); if (clonedObj) { destDict->setAt(pName, clonedObj, tmpObjId); clonedObj->close(); } } } delete iter; }
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; }