Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    << Multiple Values>> in Attribute Manager

    Cinema 4D SDK
    c++ r20 sdk
    2
    3
    463
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      d_schmidt
      last edited by

      Hello! I understand that TriState is used to display <<Multiple Values>> for GeDialogs, but is there a way to simulate that functionality in the Attribute Manager when a single object is selected?

      I'm looking to have different parameters displayed depending on which object is selected in an InExclude, and if multiple objects are selected I wanted to mimic the Multiple Values behavior. Is this possible?

      Dan

      1 Reply Last reply Reply Quote 0
      • ManuelM
        Manuel
        last edited by Manuel

        hi,

        you have to use SetTristate in the function GetDParameter.
        Of course you have to be careful that TriState is allowed.

        Here's an example that will create a generator, if you add two cube as child, it will be able to change the SEGMENT_X. if both cube have different parameter, the generator will display <<Multiple Values>>
        Sorry for the un-commented code

        maxon::Int32 g_pc11644 = -1;
        
        
        #define SEGMENT_X  2000 
        
        
        
        
        class PC11644 : public ObjectData
        {
        	INSTANCEOF(PC11644, ObjectData)
        
        public:
        	
        
        
        	static NodeData* Alloc() { return NewObjClear(PC11644); }
        
        	virtual Bool Init(GeListNode* node)
        	{
        
        		node->SetParameter(DescID(SEGMENT_X), GeData(1), DESCFLAGS_SET::NONE);
        
        
        		return true;
        	}
        
        	virtual Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags)
        	{
        		// Add a Segment Parameter that will drive children parameters.
        
        		// we have no description but we can load the basic one.
        		if (!description->LoadDescription(Obase))
        			return false;
        
        		
        		const DescID* singleid = description->GetSingleDescID();
        
        		DescID cid = DescLevel(SEGMENT_X, DTYPE_LONG, 0);
        		// check if this parameter ID is requested (NOTE: this check is important for speed)
        		if (!singleid || cid.IsPartOf(*singleid, nullptr))
        		{
        			// define the new parameter description
        			BaseContainer settings = GetCustomDataTypeDefault(DTYPE_LONG);
        			settings.SetString(DESC_NAME, "Segments X"_s);
        
        			// set the new parameter
        			if (!description->SetParameter(cid, settings, ID_OBJECTPROPERTIES))
        				return false;
        		}
        
        
        		flags |= DESCFLAGS_DESC::LOADED;
        		return SUPER::GetDDescription(node, description, flags);
        
        	}
        
        
        
        
        
        
        	virtual Bool GetDParameter(GeListNode* node, const DescID& id, GeData& t_data, DESCFLAGS_GET& flags) override
        	{
        		iferr_scope_handler
        		{
        			err.DbgStop();
        			return err;
        		};
        
        		// check if tristate is allowed for this gadget
        		const Bool tristateReturnAllowed = (flags & DESCFLAGS_GET::ALLOW_TRISTATE) ? true : false;
        
        		if (id[0].id == SEGMENT_X)
        		{
        			Bool isTristate = false;
        			GeData childData;
        			
        			// Check if all children have the same value
        			iferr (isTristate = CheckChildValue(node, childData))
        			{
        				err.DiagOutput();
        			}
        			
        			if (tristateReturnAllowed && isTristate)
        			{
        				// if tristate is allowed and value are different we use SetTristate to set the GeData type to DA_TRISTATE
        				// the gadget will show 'multiple value'
        				t_data.SetTristate();
        			}
        			else if (isTristate)
        			{
        				// show default value
        				t_data.SetInt32(1);
        			}
        			else if (childData.GetType() ==  DA_LONG)
        			{
        				t_data = childData;
        			}
        
        			else
        			{	
        				// retrieves the value in the BaseContainer
        				BaseContainer *bc = static_cast<BaseObject*>(node)->GetDataInstance();
        				if (bc == nullptr)
        					return false;
        				t_data = bc->GetData(SEGMENT_X);
        				
        			}
        
        			flags |= DESCFLAGS_GET::PARAM_GET;
        
        		}
        
        		
        		return SUPER::GetDParameter(node, id, t_data, flags);
        	}
        
        
        	virtual Bool SetDParameter(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_SET& flags) override
        	{
        		iferr_scope_handler
        		{
        			err.DbgStop();
        			return false;
        
        		};
        
        		if (id[0].id == SEGMENT_X)
        		{
        
        			iferr (SetChildValues(node, t_data))
        			{
        				err.DiagOutput();
        			}
        			BaseContainer *bc = static_cast<BaseObject*>(node)->GetDataInstance();
        			if (bc == nullptr)
        				return false;
        			bc->SetData(SEGMENT_X, t_data);
        
        			flags |= DESCFLAGS_SET::PARAM_SET;
        
        
        		}
        
        
        		
        		return SUPER::SetDParameter(node, id, t_data, flags);
        	}
        
        
        
        
        
        	virtual BaseObject* GetVirtualObjects(BaseObject* op, HierarchyHelp* hh) override
        	{
        
        		GeData data;
        		
        		if (op->GetParameter(DescID(SEGMENT_X), data, DESCFLAGS_GET::NONE))
        		{
        			if (data.GetType() == DA_LONG)
        				DiagnosticOutput("Type is still DA_LONG with value @", data.GetInt32());
        			else
        				DiagnosticOutput("Type is not DA_LONG anymore");
        		}
        		
        		
        		
        
        
        
        		BaseObject* parent = BaseObject::Alloc(Onull);
        		return parent;
        	}
        
        
        	private:
        
        
        		/// Retrieve the direct child if they are cubes
        		/// @param[in] in_node the parent of children
        		maxon::Result<maxon::BaseArray<BaseObject*>> GetChildren(GeListNode* in_node) const
        		{
        
        			iferr_scope;
        			// Cast the node to a BaseObject
        			BaseObject* op = static_cast<BaseObject*>(in_node);
        			if (in_node == nullptr)
        				return maxon::NullptrError(MAXON_SOURCE_LOCATION);
        
        
        			// Create an array to add the children
        			maxon::BaseArray<BaseObject*> nodeList;
        
        			// return an error if there's no child
        			BaseObject* child = op->GetDown();
        			if (child == nullptr)
        			{
        				return maxon::NullptrError(MAXON_SOURCE_LOCATION);
        			}
        
        			// Add the children to the array
        			while (child)
        			{
        				if (child->IsInstanceOf(Ocube))
        				{
        					nodeList.Append(child) iferr_return;
        				}
        				child = child->GetNext();
        			}
        
        			// return the array
        			return nodeList;
        		}
        
        		///
        		/// Return if the children's value are all the same (for PRIM_CUBE_SUBX)
        		/// @param[in] in_node the parent of children
        		/// @param[out] the value stored for the first children
        		///
        		maxon::Result<Bool> CheckChildValue(GeListNode* in_node, GeData& out_childData) const
        		{
        			iferr_scope;
        
        			// Retrieves the children of the node
        			iferr (const maxon::BaseArray<BaseObject*> nodeList = GetChildren(in_node))
        			{
        				return err;
        			}
        			
        			// Retrieve the parameter of the first child
        			if (!nodeList[0]->GetParameter(PRIM_CUBE_SUBX, out_childData, DESCFLAGS_GET::NONE))
        			{
        				return maxon::UnknownError(MAXON_SOURCE_LOCATION);
        			}
        
        			
        
        			Bool isTristate = false;
        
        			// Check for each children if the value is different
        			for (const auto& child : nodeList)
        			{
        				GeData childData;
        				child->GetParameter(PRIM_CUBE_SUBX, childData, DESCFLAGS_GET::NONE);
        				if (childData != out_childData)
        				{
        					isTristate = true;
        				}
        			}
        
        			
        			return isTristate;
        		}
        
        		/// Set the value of node's children
        		/// @param[in] in_node the parent of children
        		/// @param[in] in_value will set the children with this value
        		maxon::Result<void> SetChildValues(GeListNode* in_node, const GeData &in_value)
        		{
        
        			iferr_scope;
        
        			iferr (maxon::BaseArray<BaseObject*> nodeList = GetChildren(in_node))
        			{
        				return err;
        			}
        
        			for (auto& child : nodeList)
        			{
        				child->SetParameter(DescID(PRIM_CUBE_SUBX), in_value, DESCFLAGS_SET::NONE);
        			}
        
        			return maxon::OK;
        
        		}
        
        };
        
        

        Cheers,
        Manuel

        MAXON SDK Specialist

        MAXON Registered Developer

        1 Reply Last reply Reply Quote 1
        • D
          d_schmidt
          last edited by

          Thanks! That's exactly what I needed!

          1 Reply Last reply Reply Quote 0
          • First post
            Last post