ObjectARX Ownership Example

// Class declarations
//
class AsdkOwnerDemo : public AcDbObject
// This is a custom object class to demonstrate what is
// necessary to create ownership trees.
// 
// To keep it simple, this class has two data members: a
// simple integer to represent normal data, and a hard
// ownership ID data member to hold the object ID of an owned
// object.
// 
// Get and set functions are provided for both data members.
// 
{
public:
    ACRX_DECLARE_MEMBERS(AsdkOwnerDemo);
    AsdkOwnerDemo(): mIntval(0) {};
    AsdkOwnerDemo(const Adesk::Int16& val): mIntval(val) {};
    Adesk::Int16 intData();
    Acad::ErrorStatus setIntData(const Adesk::Int16&);
    AcDbHardOwnershipId idData();
    Acad::ErrorStatus setIdData(const AcDbHardOwnershipId&);
    Acad::ErrorStatus dwgInFields (AcDbDwgFiler*);
    Acad::ErrorStatus dwgOutFields(AcDbDwgFiler*) const;
    Acad::ErrorStatus dxfInFields (AcDbDxfFiler*);
    Acad::ErrorStatus dxfOutFields(AcDbDxfFiler*) const;
private:
    Adesk::Int16 mIntval;
    AcDbHardOwnershipId mObjId;
};
ACRX_DXF_DEFINE_MEMBERS(AsdkOwnerDemo, AcDbObject, 
    AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent, 0,
    ASDKOWNERDEMO, OWNERSHIP);
// Gets the value of the integer data member.
//
Adesk::Int16
AsdkOwnerDemo::intData()
{
    assertReadEnabled();
    return mIntval;
}
// Sets the value of the integer data member.
//
Acad::ErrorStatus
AsdkOwnerDemo::setIntData(const Adesk::Int16& val)
{
    assertWriteEnabled();
    mIntval = val;
    return Acad::eOk;
}
// Returns a copy of the ownership ID data member.
//
AcDbHardOwnershipId
AsdkOwnerDemo::idData()
{
    assertReadEnabled();
    return mObjId;
}
// Sets the value of the ownership ID data member.
//
Acad::ErrorStatus
AsdkOwnerDemo::setIdData(const AcDbHardOwnershipId& ownedId)
{
    if (ownedId.asOldId() == 0L) {
        return Acad::eInvalidInput;
    }
    assertWriteEnabled();
    mObjId = ownedId;
    // Now set the backpointer.  A transaction is used for
    // opening the object, so if the object is already
    // open it won't prevent this setting from taking place.
    //
    AcDbObject *pObj;
    AcTransaction *pTrans
        = actrTransactionManager->startTransaction();
    pTrans->getObject(pObj, ownedId, AcDb::kForWrite);
    pObj->setOwnerId(objectId());
    actrTransactionManager->endTransaction();
    return Acad::eOk;
}
// Files data in from a DWG file.
//
Acad::ErrorStatus
AsdkOwnerDemo::dwgInFields(AcDbDwgFiler* filer)
{
    assertWriteEnabled();
    AcDbObject::dwgInFields(filer);
    // For wblock filing we wrote out our owner as a hard
    // pointer Id so now we need to read it in to keep things
    // in sync.
    //
    if (filer->filerType() == AcDb::kWblockCloneFiler) {
        AcDbHardPointerId id;
        filer->readItem(&id);
    }
    filer->readItem(&mIntval);
    filer->readItem(&mObjId);
    return filer->filerStatus();
}
// Files data out to a DWG file.
//
Acad::ErrorStatus
AsdkOwnerDemo::dwgOutFields(AcDbDwgFiler* filer) const
{
    assertReadEnabled();
    AcDbObject::dwgOutFields(filer);
    // Since objects of this class will be in the Named
    // Objects Dictionary tree and may be hard referenced
    // by some other object, to support wblock we need to
    // file out our owner as a hard pointer Id so that it
    // will be added to the list of objects to be wblocked
    //
    if (filer->filerType() == AcDb::kWblockCloneFiler)
        filer->writeHardPointerId((AcDbHardPointerId)ownerId());
    filer->writeItem(mIntval);
    filer->writeItem(mObjId);
    return filer->filerStatus();
}
// Files data in from a DXF file.
//
Acad::ErrorStatus
AsdkOwnerDemo::dxfInFields(AcDbDxfFiler* filer)
{
    assertWriteEnabled();
    Acad::ErrorStatus es;
    if ((es = AcDbObject::dxfInFields(filer))
        != Acad::eOk)
    {
        return es;
    }
    // Check if we're at the right subclass data marker.
    //
    if (!filer->atSubclassData("AsdkOwnerDemo")) {
        return Acad::eBadDxfSequence;
    }
    struct resbuf inbuf;
    while (es == Acad::eOk) {
        if ((es = filer->readItem(&inbuf)) == Acad::eOk) {
            if (inbuf.restype == AcDb::kDxfInt16) {
                mIntval = inbuf.resval.rint;
            } else if (inbuf.restype
                == AcDb::kDxfHardOwnershipId)
            {
                acdbGetObjectId(mObjId,
                    inbuf.resval.rlname);
            }
        }
    }
    return filer->filerStatus();
}
// Files data out to a DXF file.
//
Acad::ErrorStatus
AsdkOwnerDemo::dxfOutFields(AcDbDxfFiler* filer) const
{
    assertReadEnabled();
    AcDbObject::dxfOutFields(filer);
    filer->writeItem(AcDb::kDxfSubclass, "AsdkOwnerDemo");
    filer->writeItem(AcDb::kDxfInt16, mIntval);
    // Null object IDs are invalid: don't write them out.
    //
    if (mObjId.asOldId() != 0L) {
        filer->writeItem(AcDb::kDxfHardOwnershipId, mObjId);
    }
    return filer->filerStatus();
}
// Creates an AsdkOwnerDemo object (pObjC) and adds data to
// it. Then, AsdkOwnerDemo pObjB is created and set to be
// the owner of pObjC. Next, AsdkOwnerDemo pObjA is created
// and is added to a dictionary in the named object dictionary. 
// Finally, pObjA is set to own pObjB. Technically,
// we could just add pObjA to the named object dictionary
// itself, but that's not appropriate because it would clutter
// up the named object dictionary.
// 
void
createObjs()
{
    AcDbObjectId objIdA, objIdB, objIdC;
    AcDbDictionary *pNamedobj;
    AcDbDictionary *pDict = NULL;
    AcDbDatabase *pCurDwg = 
        acdbHostApplicationServices()->workingDatabase();
    // Create object C with a dummy integer data value of 3.
    //
    AsdkOwnerDemo *pObjC = new AsdkOwnerDemo(3);
    // Append object C to database without setting an owner.
    //
    pCurDwg->addAcDbObject(objIdC, pObjC);
    pObjC->close();
    // Create object B with a dummy integer data value of 2.
    //
    AsdkOwnerDemo *pObjB = new AsdkOwnerDemo(2);
    // Append object B to the database without setting an owner.
    //
    pCurDwg->addAcDbObject(objIdB, pObjB);
    // Now set up ownership for object C.  The
    // AsdkOwnerDemo::setIdData() function takes the
    // objectId parameter and copies it into the
    // AcDbHardOwnershipId data member.  This places the
    // object ID in a position to be filed out/in via the
    // dwgInFields/dwgOutFields/dxfInFields/dxfOutFields
    // member functions.  This constitutes primary
    // "ownership."  The AsdkOwnerDemo::setIdData() function
    // also calls each owned object's setOwnerId() member
    // function to set the backpointer and establish the
    // full two-way ownership link.
    // 
    pObjB->setIdData(objIdC);
    pObjB->close();
    // Create object A with a dummy integer data value of 1.
    //
    AsdkOwnerDemo *pObjA = new AsdkOwnerDemo(1);
    // Next, add objA to a dictionary in the named object
    // dictionary.  This will establish ownership for objA,
    // set the ownership backlink, and add it to the
    // database.
    //
    pCurDwg->getNamedObjectsDictionary(pNamedobj,
        AcDb::kForWrite);
    // Get a pointer to the ASDK_DICT dictionary.  If it
    // doesn't exist, then create it and add it to the
    // named object dictionary.
    //
    if (pNamedobj->getAt("ASDK_DICT", (AcDbObject*&) pDict,
        AcDb::kForWrite) == Acad::eKeyNotFound)
    {
        pDict = new AcDbDictionary;
        AcDbObjectId DictId;
        pNamedobj->setAt("ASDK_DICT", pDict, DictId);
    }
    pNamedobj->close();
    // Add object A to the ASDK_DICT dictionary.
    //
    pDict->setAt("OBJA", pObjA, objIdA);
    pDict->close();
    // Now set up ownership for object B.
    //
    pObjA->setIdData(objIdB);
    pObjA->close();
}
// The list tree function runs through all objects in the 
// ASDK_DICT dictionary, follows their ownership trees, and 
// lists out information on all objects in the tree.
// 
void
listTree()
{
    AcDbDictionary *pNamedobj;
    AcDbDictionary *pDict;
    acdbHostApplicationServices()->workingDatabase()
        ->getNamedObjectsDictionary(pNamedobj, AcDb::kForWrite);
    // Get a pointer to the ASDK_DICT dictionary.
    //
    pNamedobj->getAt("ASDK_DICT",(AcDbObject*&)pDict,
        AcDb::kForRead);
    pNamedobj->close();
    // Run through the entries and list their backpointers.
    //
    AcDbDictionaryIterator *pDictItr = pDict->newIterator();
    for (; !pDictItr->done(); pDictItr->next()) {
        printOut(pDictItr->objectId());
    }
    pDict->close();
}
// Recursively walks down an ownership tree of AsdkOwnerDemo
// class objects, printing out information on each one.
//
void
printOut(AcDbObjectId id)
{
    AsdkOwnerDemo *pDemo;
    acdbOpenObject((AcDbObject*&)pDemo, id, AcDb::kForRead);
    acutPrintf("\nIntdata: %d  ObjId: %ld  Backpointer:"
        " %ld OwnedObj: %ld", pDemo->intData(),
        (pDemo->objectId()).asOldId(),
        (pDemo->ownerId()).asOldId(),
        (pDemo->idData()).asOldId());
    // Recursive tree walk
    //
    if ((pDemo->idData()).asOldId() != 0L) {
        printOut(pDemo->idData());
    }
    pDemo->close();
}
// The initialization function is called from acrxEntryPoint() 
// during kInitAppMsg case.  This function is used to add commands
// to the command stack and to add classes to the ACRX class
// hierarchy.
// 
void
initApp()
{
    acedRegCmds->addCommand("ASDK_OWNERSHIP_COMMANDS",
        "ASDK_CREATE", "CREATE",ACRX_CMD_MODAL, createObjs);
    acedRegCmds->addCommand("ASDK_OWNERSHIP_COMMANDS",
        "ASDK_LISTREE", "LISTREE",ACRX_CMD_MODAL, listTree);
    AsdkOwnerDemo::rxInit();
    acrxBuildClassHierarchy();
}
// The clean up function is called from acrxEntryPoint() during the
// kUnloadAppMsg case.  This function removes this application's
// command set from the command stack and the
// AsdkOwnerDemo class from the ARX runtime class tree.
// 
void
unloadApp()
{
    acedRegCmds->removeGroup("ASDK_OWNERSHIP_COMMANDS");
    // Remove the AsdkOwnerDemo class from the ACRX runtime
    // class hierarchy.  If this is done while the database is
    // still active, it should cause all objects of class
    // AsdkOwnerDemo to be turned into proxies.
    // 
    deleteAcRxClass(AsdkOwnerDemo::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;
}