- Main Page
- Related Pages
- Modules
- Classes
- Examples

# dagMessageCmd.cpp

    //-
    // ==========================================================================
    // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+

    //
    // dagMessageCmd.cpp
    //
    // Description:
    //     Sample plug-in that demonstrates how to register/de-register
    //     a callback with the MDagMessage class.
    //
    //     This plug-in will register a new command in maya called
    //     "dagMessage" which adds a callback for the all nodes on
    //     the active selection list. A message is printed to stdout
    //     whenever a connection is made or broken for those nodes. If
    //      nothing is selected, the callback will be for all nodes.
    //
    //     dagMessage -help will list the options.
    //
    #include <maya/MIOStream.h>
    #include <maya/MPxCommand.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MArgList.h>
    #include <maya/MArgDatabase.h>
    #include <maya/MIntArray.h>
    #include <maya/MSelectionList.h>
    #include <maya/MGlobal.h>
    #include <maya/MPlug.h>
    #include <maya/MSyntax.h>
    #include <maya/MDagMessage.h>
    #include <maya/MDGMessage.h>
    #include <maya/MModelMessage.h>
    #include <maya/MDagPath.h>
    #include <maya/MFnDagNode.h>
    #include <maya/MCallbackIdArray.h>
    #include <maya/MObjectArray.h>

    #define kCmdName                    "dagMessage"

    #define kAllDagFlag                 "-ad"
    #define kAllDagFlagLong             "-allDag"
    #define kParentAddedFlag            "-pa"
    #define kParentAddedFlagLong        "-parentAdded"
    #define kParentRemovedFlag          "-pr"
    #define kParentRemovedFlagLong      "-parentRemoved"
    #define kChildAddedFlag             "-ca"
    #define kChildAddedFlagLong         "-childAdded"
    #define kChildRemovedFlag           "-cr"
    #define kChildRemovedFlagLong       "-childRemoved"
    #define kChildReorderedFlag         "-cro"
    #define kChildReorderedFlagLong     "-childReordered"

    #define kHelpFlag                   "-h"
    #define kHelpFlagLong               "-help"

    #define CheckErrorContinue(stat, msg)   \
        if (MS::kSuccess != stat) {         \
            displayError(msg);              \
            continue;                       \
        }

    #define CheckErrorReturn(stat, msg)     \
        if (MS::kSuccess != stat) {         \
            displayError(msg);              \
            return;                         \
        }

    // This table will keep track of the registered callbacks
    // so they can be removed when the plug-ins is unloaded.
    //
    MCallbackIdArray callbackIds;

    // Node added to model callback.
    static void userNodeRemovedCB(MObject& node,void *clientData)
    {
        if (! node.isNull()) {
            bool doDisplay = true;

            MStatus status;
            MFnDagNode dagNode(node,&status);
            if ( status.error() ) {
                doDisplay = false;
                MGlobal::displayInfo("Error: failed to get dag node.");
            }

            if ( doDisplay ) {
                MString s = dagNode.name();
                MString info("DAG Model -  Node removed: ");
                info+= s;

                MGlobal::displayInfo(info);
            }
        }

        // remove the callback
        MCallbackId id = MMessage::currentCallbackId();
        MMessage::removeCallback(id);
    }

    // Node added to model callback.
    static void userNodeAddedCB(MObject& node,void *clientData)
    {
        MStatus status;

        if (! node.isNull()) {
            bool doDisplay = true;

            MDagPath path;
            status = MDagPath::getAPathTo(node,path);
            if ( status.error() ) {
                doDisplay = false;
                MGlobal::displayInfo("Error: failed to get dag path to node.");
            }

            if ( doDisplay ) {
                MString s = path.fullPathName();
                MString info("DAG Model -  Node added: ");
                info+= s;

                path.transform(&status);
                if (MS::kInvalidParameter == status) {
                    info += "(WORLD)";
                }

                MGlobal::displayInfo(info);
            }
        }

        // remove the callback
        MCallbackId id = MMessage::currentCallbackId();
        MMessage::removeCallback(id);

        // listen for removal message
        /* MCallbackId id = */ MModelMessage::addNodeRemovedFromModelCallback( node, userNodeRemovedCB, 0, &status );
        if ( status.error() ) {
            MGlobal::displayError("Failed to install node removed from model callback.\n");
            return;
        }
    }

    // Install a node added callback for the node specified
    // by dagPath.
    static void installNodeAddedCallback( MDagPath& dagPath )
    {
        MStatus status;

        MObject dagNode = dagPath.node();
        if ( dagNode.isNull() )
            return;

        /* MCallbackId id = */ MModelMessage::addNodeAddedToModelCallback( dagNode, userNodeAddedCB, 0, &status );
        if ( status.error() ) {
            MGlobal::displayError("Failed to install node added to model callback.\n");
            return;
        }
    }

    // Decide if the dag is in the model. Dag paths and names
    // may not be setup if the dag has not been added to
    // the model.
    static bool dagNotInModel( MDagPath& dagPath )
    {
        MStatus status;
        MFnDagNode dagFn( dagPath, &status );
        if ( status.error() )
            return false;
        bool inModel = dagFn.inModel( &status );
        if ( status.error() )
            return false;
        return ( inModel == false );
    }

    void userDAGGenericCB(MDagMessage::DagMessage msg, MDagPath &child,
                          MDagPath &parent, void *)
    {
        MString dagStr("DAG Changed - ");
        switch (msg) {
            case MDagMessage::kParentAdded:
                dagStr += "Parent Added: ";
                break;
            case MDagMessage::kParentRemoved:
                dagStr += "Parent Removed: ";
                break;
            case MDagMessage::kChildAdded:
                dagStr += "Child Added: ";
                break;
            case MDagMessage::kChildRemoved:
                dagStr += "Child Removed: ";
                break;
            case MDagMessage::kChildReordered:
                dagStr += "Child Reordered: ";
                break;
            default:
                dagStr += "Unknown Type: ";
                break;
        }

        dagStr += "child = ";
        dagStr += child.fullPathName();
        dagStr += ", parent = ";
        dagStr += parent.fullPathName();

        // Check to see if the parent is the world object.
        //
        MStatus pStat;
        parent.transform(&pStat);
        if (MS::kInvalidParameter == pStat) {
            dagStr += "(WORLD)";
        }

        // Install callbacks if node is not in the model.
        // Callback is for node added to model.
        bool incomplete = false;
        if ( dagNotInModel( child ) ) {
            installNodeAddedCallback( child );
            incomplete = true;
        }
        if ( dagNotInModel( parent ) ) {
            installNodeAddedCallback( parent);
            incomplete = true;
        }

        // Warn user that dag path info may be
        // incomplete
        if (incomplete)
            dagStr += "\t// May be incomplete!";

        MGlobal::displayInfo(dagStr);
    }

    //
    // Command class declaration
    //

    class dagMessageCmd : public MPxCommand
    {
    public:
                        dagMessageCmd() {};
        virtual         ~dagMessageCmd();
        MStatus         doIt( const MArgList& args );

        static MSyntax  newSyntax();
        static void*    creator();

    private:

        MStatus         addGenericCallback(MDagPath *dagPath,
                                           MDagMessage::DagMessage msg,
                                           MString cbName);
    };

    //
    // Command class implementation
    //

    dagMessageCmd::~dagMessageCmd() {}

    void* dagMessageCmd::creator()
    {
        return new dagMessageCmd();
    }

    MSyntax dagMessageCmd::newSyntax()
    {
        MSyntax syntax;

        syntax.useSelectionAsDefault(true);
        syntax.setObjectType(MSyntax::kSelectionList);
        syntax.setMinObjects(0);

        syntax.addFlag(kAllDagFlag, kAllDagFlagLong);
        syntax.addFlag(kParentAddedFlag, kParentAddedFlagLong);
        syntax.addFlag(kParentRemovedFlag, kParentRemovedFlagLong);
        syntax.addFlag(kChildAddedFlag, kChildAddedFlagLong);
        syntax.addFlag(kChildRemovedFlag, kChildRemovedFlagLong);
        syntax.addFlag(kChildReorderedFlag, kChildReorderedFlagLong);
        syntax.addFlag(kHelpFlag, kHelpFlagLong);
        return syntax;
    }

    MStatus dagMessageCmd::addGenericCallback(MDagPath *dagPath,
                                              MDagMessage::DagMessage msg,
                                              MString cbName)
    {
        MStatus status = MS::kFailure;

        if (NULL == dagPath) {
            MCallbackId id = MDagMessage::addDagCallback(   msg,
                                                            userDAGGenericCB,
                                                            NULL,
                                                            &status);
            if (MS::kSuccess == status) {
                MString info("Adding a callback for");
                info += cbName;
                info += "on all nodes";
                MGlobal::displayInfo(info);
                callbackIds.append( id );
            } else {
                MString err("Could not add callback to");
                err += dagPath->fullPathName();
                MGlobal::displayError(err);
            }
        } else {
            MCallbackId id = MDagMessage::addDagCallback(*dagPath,
                                                        msg,
                                                        userDAGGenericCB,
                                                        NULL,
                                                        &status);
            if (MS::kSuccess == status) {
                MString info("Adding a callback for");
                info += cbName;
                info += "on ";
                info += dagPath->fullPathName();
                MGlobal::displayInfo(info);
                callbackIds.append( id );
            } else {
                MString err("Could not add callback to");
                err += dagPath->fullPathName();
                MGlobal::displayError(err);
            }
        }

        return status;
    }

    MStatus dagMessageCmd::doIt( const MArgList& args)
    //
    // Takes the  nodes that are on the active selection list and adds an
    // attriubte changed callback to each one.
    //
    {
        MStatus         status;
        MSelectionList  list;
        MArgDatabase argData(syntax(), args);

        status = argData.getObjects(list);
        if (MS::kSuccess != status) {
            MGlobal::displayError("Error getting objects");
            return status;
        }

        //  Get the flags
        //
        bool allDagUsed = argData.isFlagSet(kAllDagFlag);
        bool parentAddedUsed = argData.isFlagSet(kParentAddedFlag);
        bool parentRemovedUsed = argData.isFlagSet(kParentRemovedFlag);
        bool childAddedUsed = argData.isFlagSet(kChildAddedFlag);
        bool childRemovedUsed = argData.isFlagSet(kChildRemovedFlag);
        bool childReorderedUsed = argData.isFlagSet(kChildReorderedFlag);
        bool helpUsed = argData.isFlagSet(kHelpFlag);

        bool nothingSet = ( !allDagUsed && !parentAddedUsed &&
                            !parentRemovedUsed && !childAddedUsed &&
                            !childRemovedUsed && !childReorderedUsed &&
                            !helpUsed);

        if (nothingSet) {
            MGlobal::displayError("A flag must be used. dagMessage -help for availible flags.");
            return MS::kFailure;
        }

        if (argData.isFlagSet(kHelpFlag)) {
            MGlobal::displayInfo("dagMessage -help");
            MGlobal::displayInfo("\tdagMessage adds a callback to the selected nodes,");
            MGlobal::displayInfo("\tor if no nodes are selected, to all nodes. The callback");
            MGlobal::displayInfo("\tprints a message when called. When the plug-in is unloaded");
            MGlobal::displayInfo("\tthe callbacks are removed.");
            MGlobal::displayInfo("");
            MGlobal::displayInfo("\t-h -help : This message is printed");
            MGlobal::displayInfo("\t-ad -allDag : parent changes and child reorders");
            MGlobal::displayInfo("\t-pa -parentAdded : A parent is added");
            MGlobal::displayInfo("\t-pr -parentRemoved : A parent is removed");
            MGlobal::displayInfo("\t-ca -childAdded : A child is added (only for individual nodes)");
            MGlobal::displayInfo("\t-cr -childRemoved : A child is removed (only for individual nodes)");
            MGlobal::displayInfo("\t-cro -childReordered : A child is reordered");
            MGlobal::displayInfo("");
        }

        unsigned nObjs = list.length();
        if (nObjs == 0) {
            //  Add the callback for all changes of the specified type.
            //
            if (allDagUsed) {
                MCallbackId id = MDagMessage::addAllDagChangesCallback(userDAGGenericCB, NULL, &status);
                if (status) {
                    callbackIds.append( id );
                    MGlobal::displayInfo("Added a callback for all Dag changes on all nodes.\n");
                } else {
                    MGlobal::displayError("Could not add a -allDag callback");
                    return status;
                }
            }

            if (parentAddedUsed) {
                status = addGenericCallback(NULL,
                                            MDagMessage::kParentAdded,
                                            MString(" parent added "));
                if (MS::kSuccess != status) {
                    return status;
                }
            }

            if (parentRemovedUsed) {
                status = addGenericCallback(NULL,
                                            MDagMessage::kParentRemoved,
                                            MString(" parent removed "));
                if (MS::kSuccess != status) {
                    return status;
                }
            }

            if (childAddedUsed) {
                MGlobal::displayError("-childAdded can only be used when a node is selected");
                status = MS::kFailure;
                return status;
            }

            if (childRemovedUsed) {
                MGlobal::displayError("-childRemoved can only be used when a node is selected");
                status = MS::kFailure;
                return status;
            }

            if (childReorderedUsed) {
                status = addGenericCallback(NULL,
                                            MDagMessage::kChildReordered,
                                            MString(" child reordered "));
                if (MS::kSuccess != status) {
                    return status;
                }
            }
        } else {
            for (unsigned int i=0; i< nObjs; i++) {
                MDagPath dagPath;
                list.getDagPath(i, dagPath);

                if (!dagPath.isValid()) {
                    continue;
                }

                //  Add the callback for all changes of the specified type.
                //
                if (allDagUsed) {
                    MCallbackId id = MDagMessage::addAllDagChangesCallback(dagPath, userDAGGenericCB, NULL, &status);
                    if (status) {
                        callbackIds.append( id );
                        MString infoStr("Added a callback for all Dag changes on ");
                        infoStr += dagPath.fullPathName();
                        MGlobal::displayInfo(infoStr);
                    } else {
                        MGlobal::displayError("Could not add a -allDag callback");
                        return status;
                    }
                }

                if (parentAddedUsed) {
                    status = addGenericCallback(&dagPath,
                                                MDagMessage::kParentAdded,
                                                MString(" parent added "));
                    if (MS::kSuccess != status) {
                        return status;
                    }
                }

                if (parentRemovedUsed) {
                    status = addGenericCallback(&dagPath,
                                                MDagMessage::kParentRemoved,
                                                MString(" parent removed "));
                    if (MS::kSuccess != status) {
                        return status;
                    }
                }

                if (childAddedUsed) {
                    status = addGenericCallback(&dagPath,
                                                MDagMessage::kChildAdded,
                                                MString(" child added "));
                    if (MS::kSuccess != status) {
                        return status;
                    }
                }

                if (childRemovedUsed) {
                    status = addGenericCallback(&dagPath,
                                                MDagMessage::kChildRemoved,
                                                MString(" child removed "));
                    if (MS::kSuccess != status) {
                        return status;
                    }
                }

                if (childReorderedUsed) {
                    status = addGenericCallback(&dagPath,
                                                MDagMessage::kChildReordered,
                                                MString(" child reordered "));
                    if (MS::kSuccess != status) {
                        return status;
                    }
                }
            }
        }

        return status;
    }

    //
    // Plugin registration
    //

    MStatus initializePlugin( MObject obj )
    {
        MFnPlugin plugin( obj );
        return plugin.registerCommand( kCmdName,
                                        dagMessageCmd::creator,
                                        dagMessageCmd::newSyntax);
    }

    MStatus uninitializePlugin( MObject obj)
    {
        // Remove callbacks
        //
        for (unsigned int i=0; i<callbackIds.length(); i++ ) {
            MMessage::removeCallback( callbackIds[i] );
        }

        MFnPlugin plugin( obj );
        return plugin.deregisterCommand( kCmdName );
    }