SOLVED << 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!