Editor Reactor Notification Functions

The AcEditorReactor class provides four notification functions that return control to the application at certain points in the deep clone operation. The following functions are called during all deep clone and wblock clone operations:

The beginDeepClone() function is called after the AcDbIdMapping instance is created and before any objects are cloned. The ID map will be empty, but it can be queried for destDb() and deepCloneContext() at this time.

The beginDeepCloneXlation() function is called after all of the objects in the primary selection set have been cloned and before the references are translated. This is the first time it is possible to see the entire set of what was cloned in the ID map. It is also the time to clone any additional objects and add them to the ID map. Remember that any objects cloned have their object IDs in flux at this point.

The abortDeepClone() function is called at any time between beginDeepClone() and endDeepClone().

The endDeepClone() function is called at the end of the cloning and translation process. The object IDs are no longer in flux. However, this call does not mean that the entities are in their final state for whatever command is being executed. Often the cloned entities are transformed or other operations are performed following the cloning process. There are additional callback functions that can be used to access the entities later, including commandEnded().

In addition to the previous four functions, the following notification functions are provided in the wblock clone operation:

These calls come in the following order with the deep clone functions:

  1. wblockNotice() This call is sent before any wblock processing has taken place. It allows the application the opportunity to call AcDbDatabase::forceWblockDatabaseCopy() in order to force cloning for a wblock* operation.
  2. beginDeepClone() This call is sent as soon as the destination AcDbDatabase instance has been created, but it is in a “raw” state and is not ready for appending.
  3. beginWblock() The new database now has its basic elements, such as a handle table, a class ID map, and model space and paper space block table records. It is still empty. The cloning has not begun, but the new database is now ready for appending.
  4. otherWblock() and beginDeepCloneXlation() These two calls are made back-to-back and can be used for the same purpose. The primary set of objects has been cloned, but the reference translation has not started yet.
  5. endDeepClone() The translation process has now completed, but the entities are not yet in their final state.
  6. endWblock() The entities have now been transformed, and the model space and paper space origins have been set. The new database is complete but has not yet been saved.

beginWblockObjects() is only sent when the AcDbDatabase::wblockCloneObjects() function has been called and is executing. It is sent once for each source database just before the first object from that database is cloned. For wblockCloneObjects(), the notification sequence is:

  1. beginDeepClone()
  2. beginWblockObjects()
  3. ...
  4. beginWblockObjects()
  5. endDeepClone

There are three types of AcEditorReactor::beginWblock(). They are listed here along with their corresponding AcDbDatabase functions:

WBLOCK*

virtual void
AcEditorReactor::beginWblock(
    AcDbDatabase* pTo,
    AcDbDatabase* pFrom)
 
Acad::ErrorStatus
AcDbDatabase::wblock(
    AcDbDatabase*& pOutputDatabase)

WBLOCK of a user-defined block

virtual void
AcEditorReactor::beginWblock(
    AcDbDatabase* pTo,
    AcDbDatabase* pFrom,
    AcDbObjectId blockId)
 
Acad::ErrorStatus 
AcDbDatabase::wblock(
    AcDbDatabase*& pOutputDatabase, 
    AcDbObjectId nObjId)

WBLOCK of a selection set

virtual void
AcEditorReactor::beginWblock(
    AcDbDatabase* pTo, 
    AcDbDatabase* pFrom, 
    const AcGePoint3d& insertionPoint)
 
Acad::ErrorStatus
AcDbDatabase::wblock(
    AcDbDatabase*& pOutputDatabase,
    const AcDbObjectIdArray& pIdSet,
    const AcGePoint3d& pPoint3d)

All three versions clone both the model space and paper space AcDbBlockTableRecord before calling beginWblock(). However, for the entities within these block table records, the order of notification will appear to come differently in the first type and last two types. In version one, entities in model space that are being cloned will receive the call for wblockClone() before the AcEditorReactor::beginWblock(). In versions two and three, entities in the AcDbBlockTableRecord or the selection set will get their wblockClone() call after the AcEditorReactor::beginWblock() notification call.

Objects that have been cloned during a partial XBIND are automatically redirected just after endDeepClone() notification. This means that their AcDbObjectIds in the externally referenced database are forwarded to the AcDbObjectIds of the clone objects in the host drawing, and the objects in the externally referenced database are deleted. Objects that reference the forwarded AcDbObjectIds end up referencing the clones in the host drawing. If you need to disable this automatic redirection for your objects, then remove the idPair() from the idMap, for your cloned objects, during endDeepClone() notification.

The following function calls occur during an INSERT or INSERT* command:

These calls come in the following order with the deep clone functions:

  1. beginInsert() and beginDeepClone() These calls come back-to-back and can be used for the same purpose.
  2. otherInsert() and beginDeepCloneXlation() These calls also come back-to-back and can be used for the same purpose.
  3. endDeepClone() The cloning and translation processes are completed. The entities are cloned but have not been appended to a block, so they are not graphical. You cannot use the entities in a selection set yet.
  4. endInsert() The entities have now been transformed and have been appended to a block. If this is an INSERT*, they are now in model space and have their graphics. They can be used in selection sets. However, if this is an INSERT, they have only been appended to a block table record; that record has not yet been added to the block table. In this case, you must wait until commandEnded() notification to use these entities in a selection set.

The sample code in this section uses the beginDeepCloneXlation() notification function. This sample illustrates how you could write a reactor to add behavior to the WBLOCK command to tell it to include all text styles in the new drawing, instead of only the text styles that are referenced by the entities. It thus shows how to use wblock with nonentities.

AcDbIdMapping has a function, deepCloneContext(), which returns the context in which the deep clone function was called. The contexts are the following:

kDcCopy

Copying within a database; uses COPY, ARRAY, MIRROR (if you are not deleting the original), LEADER acquisition, or copy of an INSERT

kDcExplode

EXPLODE of a block reference

kDcBlock

BLOCK creation

kDcXrefBind

XREF Bind and XBIND

kDcSymTableMerge

XREF Attach, Resolve, and Reload

kDcSaveAs

SAVEAS when VISRETAIN is set to 1 (only symbol table records are cloned here)

kDcInsert

INSERT of a drawing

kDcWblock

WBLOCK

kDcObjects

AcDbDatabase::deepCloneObjects()

The AcEditorReactor::abortDeepClone() function is called when a call to AcDbDatabase::abortDeepClone() is made.

The following code uses a transient editor reactor derived from AcEditorReactor and overrides the beginDeepCloneXlation() function for the reactor.

//  Since AcDbDatabase::wblock() only supports AcDbEntities
//  in its array of IDs, this code demonstrates how to add
//  additional objects during beginDeepCloneXlation(). If
//  it is a WBLOCK command, it asks the user if all text
//  styles should be wblocked.  Otherwise, only those text
//  styles referenced by entities being wblocked
//  will be included (wblock's default behavior).
// AsdkEdReactor is derived from AcEditorReactor.
//
void AsdkEdReactor::beginDeepCloneXlation(AcDbIdMapping& idMap,
    Acad::ErrorStatus* es)
{
    if (idMap.deepCloneContext() == AcDb::kDcWblock
        && getYorN("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;
            }
            // It is not necessary 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();
    }
}