<< Multiple Values>> in Attribute Manager



  • 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



  • 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



  • 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



  • Thanks! That's exactly what I needed!


Log in to reply