//- // ========================================================================== // Copyright (C) 1995 - 2005 Alias Systems Corp. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files are provided by Alias // Systems Corp. ("Alias") and/or its licensors for the exclusive use of the // Customer (as defined in the Alias Software License Agreement that // accompanies this Alias software). Such Customer has the right to use, // modify, and incorporate the Data into other products and to distribute such // products for use by end-users. // // THE DATA IS PROVIDED "AS IS". ALIAS HEREBY DISCLAIMS ALL WARRANTIES // RELATING TO THE DATA, INCLUDING, WITHOUT LIMITATION, ANY AND ALL EXPRESS OR // IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. IN NO EVENT SHALL ALIAS BE LIABLE FOR ANY DAMAGES // WHATSOEVER, WHETHER DIRECT, INDIRECT, SPECIAL, OR PUNITIVE, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, OR IN EQUITY, // ARISING OUT OF ACCESS TO, USE OF, OR RELIANCE UPON THE DATA. // ========================================================================== //+ // lockEvent.cpp // // This plug-in demonstrates the API callbacks for node and plug locking. // These callbacks allow you to receive notification when the locked // status of a plug or node is queried internally. The API programmer has // option, upon receipt of the callback, to 'override' the lock state of // node or plug. This is 'override' is controlled via a 'decision' variable // passed into the callback function. The variable can hold two values // // 1) decision = true --> You want to accept the lock state and do // whatever the internal default behavior is. // 2) decision = false --> You want to deny the lock state and do the // opposite of what Maya would usually do. // // The flow of execution would be as follows ... // // 1) Received a callback from Maya. // 2) What kind of event is this? // 3) Do I want to allow this event? // |-- 4) Yes, I do not want to OVERRIDE this event. decision = true. // |-- 4) No, I want to OVERRIDE this event. decision = false. // 5) Return from callback. // // // Example usage: // sphere ; // // // Watch the translateX plug on the sphere we just created ... // lockEvent -a 3 nurbsSphere1.translateX; // // Do not allow any changes to the plug. // lockEvent -o true // // // Now you can try changes nurbsSphere1.translateX 's value // // but you will not be allowed to do so. // // // setAttr "nurbsSphere1.translateX" 22 // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ///////// // Macro Definitions // #define MEL_COMMAND_NAME "lockEvent" #define VENDOR_TAG "Alias" #define PLUGIN_VERSION "1.0" #define checkStdError(stat,msg) \ if ( MS::kSuccess != stat ) { \ cerr << msg; \ return MS::kFailure; \ } //////// // Flags // // Clear the registered callbacks ... // #define kClearCBLong "-clearCB" #define kClearCB "-ccb" #define kClearCBDV false // Toggle the override flag. Dictates if we should // obey the lock or override the lock // #define kOverrideLong "-override" #define kOverride "-o" #define kOverrideDV false // Attach a new callback to a node. // #define kAttachLong "-attach" #define kAttach "-a" #define kAttachDV 0 // Macro used to save a little typing // #define MLM MLockMessage ///////// // Class Definition // class lockEvent : public MPxCommand { public: lockEvent( ); virtual ~lockEvent( ); MStatus doIt( const MArgList & ); MStatus parseArgs( const MArgList &args ); MCallbackId installCallback( MItSelectionList & ); static MSyntax newSyntax( ); static void* creator( ); // Clear callback ids static bool clearCallbackIds(); private: MSelectionList theList; unsigned int fAttach; bool fOverrideFlag, fOverrideVal, fClearCB; }; static MCallbackIdArray callbackIds; static bool overrideMode = false; // Callback for DAG locking events. // void lockDagDecision( MDagPath &path, MDagPath &other, void *clientData, MLM::LockDAGEvent, bool &decision ); // Other, node related events .. // void lockDecision( MObject &node, MObject &aux, void *clientData, MLM::LockEvent, bool &decision ); // Events relating to plugs. // void plugDecision( MPlug &p1, MPlug &p2, void *clientData, MLM::LockPlugEvent, bool &decision ); // Callback for global watchers (i.e. watch all plugs on a node). // void nodePlugDecision( MPlug &p1, MPlug &p2, void *clientData, MLM::LockPlugEvent, bool &decision ); // Standard API entry and exit points. // MStatus initializePlugin( MObject obj ); MStatus uninitializePlugin( MObject obj ); // -------- // METHODS // lockEvent::lockEvent( ) : fOverrideFlag(kOverrideDV), fOverrideVal(false), fAttach(kAttachDV) { } lockEvent::~lockEvent( ) { } MCallbackId lockEvent::installCallback( MItSelectionList &iter ) // // Description: // Uses given iterator and callback type to attach a new callback on // a node, dag path, or plug. The selection iterator must contain a // valid selection item for the target callback type (fAttach). That is, // if the callback type is three, then the iterator must contain // a dependency node on it the next list item. // { MStatus status; MCallbackId id = 0; MObject node, component; MDagPath path; switch (fAttach) { case 1: { status = iter.getDependNode( node ); if ( status ) { // Try to set the callback. Note: we check the status // flag at the end of the switch statement. // id = MLM::setNodeLockQueryCallback( node, lockDecision, NULL, &status ); } } break; case 2: { status = iter.getDagPath( path, component ); if ( status ) { // Try to set the callback. Note: we check the status // flag at the end of the switch statement. // id = MLM::setNodeLockDAGQueryCallback( path, lockDagDecision, NULL, &status ); } } break; case 3: { status = iter.getDependNode( node ); MStringArray plugName; iter.getStrings( plugName ); // Now we have to parse the plug string. // if ( status && plugName.length() > 0 ) { MFnDependencyNode depNode( node ); MStringArray attrName; plugName[0].split( '.', attrName ); MPlug plug = depNode.findPlug( attrName[1], &status ); if ( status ) { // Try to set the callback. Note: we check the status // flag at the end of the switch statement. // id = MLM::setPlugLockQueryCallback( plug, plugDecision, NULL,&status ); } } else { status = MS::kFailure; } } break; case 4: { status = iter.getDependNode( node ); if ( status ) { // Try to set the callback. Note: we check the status // flag at the end of the switch statement. // id = MLM::setPlugLockQueryCallback( node, nodePlugDecision, NULL, &status ); } } break; default: MGlobal::displayError( "Invalid callback attach type" ); status = MS::kFailure; }; MFnDependencyNode fnNode( node ); // Check the status flag here and report any particular problems // encountered. It is possible for the callback attach routines // to fail. // // This typically occurs when a callback has already been attached // to the node or plug. // if ( !status || !id ) { MString msg; msg = "Unable to add callback for node "; msg += fnNode.name(); MGlobal::displayError( msg ); status.perror( msg ); } else { // Store the result -- so we can clean up later -- and // echo some useful information. // cerr << "Callback attached to " << fnNode.name(); cerr << "; attachment type = " << fAttach << endl; callbackIds.append( (int)id ); } return id; } MStatus lockEvent::doIt( const MArgList &args ) // // Description: // Entry point // { MStatus status; int result = 0; // First check our arguments // if ( !parseArgs( args ) ) { return MS::kFailure; } if ( fAttach ) { MItSelectionList iter( theList, MFn::kDependencyNode, &status ); for ( ; status && !iter.isDone(); iter.next() ) { MCallbackId id = installCallback( iter ); if ( id ) { result ++; } else { status = MS::kFailure; } } } else if ( fOverrideFlag ) { // What to do when callback occurs. overrideMode = fOverrideVal; } else if ( fClearCB ) { clearCallbackIds(); result++; } clearResult(); // Let the caller know if the operation was successful. // We just use an integer value here. Anything > 0 is // a success. // setResult( result ); return status; } void *lockEvent::creator( ) // // Description: // Create a new instance of this command. // { return new lockEvent; } MStatus lockEvent::parseArgs( const MArgList &args ) { MStatus status; MArgDatabase argData( syntax(), args ); fAttach = kAttachDV; fOverrideFlag = kOverrideDV; fClearCB = kClearCBDV; // begin-parse-args if ( argData.isFlagSet( kClearCB ) ) { fClearCB = !kClearCBDV; } if ( argData.isFlagSet( kOverride ) ) { bool tmp; status = argData.getFlagArgument( kOverride, 0, tmp ); if ( !status ) { MGlobal::displayError( "override flag parsing failed" ); return status; } fOverrideFlag = !kOverrideDV; fOverrideVal = tmp; } if ( argData.isFlagSet( kAttach ) ) { unsigned int tmp; status = argData.getFlagArgument( kAttach, 0, tmp ); if ( !status ) { MGlobal::displayError( "attach flag parsing failed" ); return status; } fAttach = tmp; } if ( fAttach ) { status = argData.getObjects( theList ); if ( theList.length() == 0 ) { MString msg = "You must specify a node/plug to attach to!"; MGlobal::displayError(msg); status = MS::kFailure; } } // Ensure that the caller did not specify too many arguments! // if ( status && fAttach && fOverrideFlag ) { MString msg = "You specified too many flags!" ; MGlobal::displayError(msg); status = MS::kFailure; } // end-parse-args return status; } MSyntax lockEvent::newSyntax( ) { MSyntax syntax; // begin-syntax syntax.addFlag( kClearCB, kClearCBLong ); syntax.addFlag( kOverride, kOverrideLong, MSyntax::kBoolean ); syntax.addFlag( kAttach, kAttachLong, MSyntax::kUnsigned ); syntax.useSelectionAsDefault( true ); syntax.setObjectType( MSyntax::kSelectionList, 0 ); // end-syntax return syntax; } void nodePlugDecision( MPlug &p1, MPlug &p2, void *clientData, MLM::LockPlugEvent event, bool &decision ) // // Description: // The watcher for plug callbacks on entire nodes. This callback // is invoked whenever lock query occurs on any plug in a node. // { MString msg, eventString; msg = "nodePlugDecision called"; // Echo the received event type. // switch (event) { case MLM::kPlugLockAttr: eventString = "kPlugLockAttr"; break; case MLM::kPlugUnlockAttr: eventString = "kPlugUnlockAttr"; break; case MLM::kPlugAttrValChange: eventString = "kPlugAttrValChange"; break; case MLM::kPlugRemoveAttr: eventString = "kPlugRemoveAttr"; break; case MLM::kPlugRenameAttr: eventString = "kPlugRenameAttr"; break; case MLM::kPlugConnect: eventString = "kPlugConnect"; break; case MLM::kPlugDisconnect: eventString = "kPlugDisconnect"; break; default: eventString = "kLastPlug"; break; }; cerr << msg << "; event = " << eventString; cerr << "; override = " << overrideMode << endl; decision = !overrideMode; } void plugDecision( MPlug &p1, MPlug &p2, void *clientData, MLM::LockPlugEvent event, bool &decision ) // // Description: // Callback function for plug locking events. This callback // is only invoked when the plug, p1, has its lock status // queried. This callback shares the same event types // as the nodePlugDecision callback. // { MString msg, eventString; msg = "plugDecision called"; switch (event) { case MLM::kPlugLockAttr: eventString = "kPlugLockAttr"; break; case MLM::kPlugUnlockAttr: eventString = "kPlugUnlockAttr"; break; case MLM::kPlugAttrValChange: eventString = "kPlugAttrValChange"; break; case MLM::kPlugRemoveAttr: eventString = "kPlugRemoveAttr"; break; case MLM::kPlugRenameAttr: eventString = "kPlugRenameAttr"; break; case MLM::kPlugConnect: eventString = "kPlugConnect"; break; case MLM::kPlugDisconnect: eventString = "kPlugDisconnect"; break; default: eventString = "kInvalidPlug"; break; }; cerr << msg << "; event = " << eventString; cerr << "; override = " << overrideMode << endl; decision = !overrideMode; } void lockDagDecision( MDagPath &path, MDagPath &other, void *clientData, MLM::LockDAGEvent event, bool &decision ) // // Description: // Callback that is invoked whenever a DAG element is involved // in a locking event. // { MString eventString; cerr << "lockDagDecision called "; switch (event) { case MLM::kGroup: eventString = "kGroup"; break; case MLM::kUnGroup: eventString = "kUnGroup"; break; case MLM::kReparent: eventString = "kReparent"; break; case MLM::kChildReorder: eventString = "kChildReorder"; break; case MLM::kCreateNodeInstance: eventString = "kCreateNodeInstance"; break; case MLM::kCreateChildInstance: eventString = "kCreateChildInstance"; break; case MLM::kCreateParentInstance: eventString = "kCreateParentInstance"; break; case MLM::kInvalidDAG: default: eventString = "kInvalid"; }; cerr << "on " << eventString << " event"; cerr << "; overrideMode = " << overrideMode << endl; decision = !overrideMode; } void lockDecision( MObject &node, MObject &attr, void *clientData, MLM::LockEvent event, bool &decision ) // // Description: // All other lock callback events are convered in this routine. // This includes everything that is not DAG related. // { MString eventString; cerr << "lockDecision called "; switch ( event ) { case MLM::kDelete: eventString = "kDelete"; break; case MLM::kRename: eventString = "kRename"; break; case MLM::kLockNode: eventString = "kLockNode"; break; case MLM::kUnlockNode: eventString = "kUnlockNode"; break; case MLM::kAddAttr: eventString = "kAddAttr"; break; case MLM::kRemoveAttr: eventString = "kRemoveAttr"; break; case MLM::kRenameAttr: eventString = "kRemoveAttr"; break; case MLM::kUnlockAttr: eventString = "kUnlockAttr"; break; case MLM::kLockAttr: eventString = "kLockAttr"; break; case MLM::kInvalid: default: eventString = "kInvalid"; }; cerr << "on " << eventString << " event"; cerr << "; overrideMode = " << overrideMode << endl; decision = !overrideMode; } bool lockEvent::clearCallbackIds( ) // // Description: // Removes all currently attached callbacks. // { unsigned int idCount = callbackIds.length(); for ( unsigned int i = 0; i < idCount; i ++ ) { cerr << "callback #" << i << "; id = " << (unsigned)callbackIds[i] << endl; MMessage::removeCallback( (MCallbackId) callbackIds[i] ); } callbackIds.clear(); return true; } MStatus initializePlugin( MObject obj ) // // Load the plugin ... // { MStatus status; MFnPlugin plugin( obj, VENDOR_TAG, PLUGIN_VERSION, "Any" ); status = plugin.registerCommand( MEL_COMMAND_NAME, lockEvent::creator, lockEvent::newSyntax ); callbackIds.clear(); return status; } MStatus uninitializePlugin( MObject obj ) // // Unload the plugin ... // { MFnPlugin plugin( obj ); MStatus status; status = plugin.deregisterCommand( MEL_COMMAND_NAME ); if ( status ) { lockEvent::clearCallbackIds(); } return status; }