1

Treeview made simple – Part 2 – Folding & Selections

In this article we will improve TreeView with Folding and Selection that is quite easy.

To handle this we have to implement some simple methods in TreeViewFunctions class and write a couple of convenience functions.
First of all we have to define te best place to store selection and folding status for our items in the tree, this is quite easy since all element in the tree are BaseList2D objects we can use BaseList bits!

Folding

Let start with folding, so implement both ShaderTree::IsOpen() and ShaderTree::Open() methods:

	virtual Bool IsOpened(void *root,void *userdata,void *obj)
	{
		if (!root || !obj)
			return FALSE;
		BaseList2D *mat = (BaseList2D*)obj;
		return 	mat->GetBit(BIT_OFOLD);
	}

so basically GetBit() return if the passed bit (BIT_OFOLD in our example ) is seted or not.
This allow Cinema4D to to understand if element is folded or not, then we need to tell to Cinema4D to fold/unfold at mouse click on the tree:

	virtual void Open(void* root, void* userdata, void* obj, Bool onoff)
	{
		if (!root || !obj)
			return;
		BaseList2D* mat = (BaseList2D*)obj;
		if(onoff)
			mat->SetBit(BIT_OFOLD);
		else
			mat->DelBit(BIT_OFOLD);
	}

This function pass Bool onoff (ask IsOpeen? and pass it) then what we need to do is set or delete bit accordingly with te status!

Ok folding is done so now you are able to collapse/uncollapse element in the tree.

Selections

The process for selection is very similar, so what we need to do is implement ShaderTree::IsSelected() and ShaderTree::Select(), but we have an additional complexity because we can have more than one selected item in the tree, in this case we will use BIT_ACTIVE. Fortunately Cinema4D help us on this topic, so first step is implement IsSelect() similar to IsOpen:

	virtual Bool IsSelected(void *root,void *userdata,void *obj)
	{
		if (!root || !obj)
			return FALSE;
		BaseList2D *mat = (BaseList2D*)obj;
		return 	mat->GetBit(BIT_ACTIVE);
	}

then for Select we have to do some additional work, Cinema4D ask us what we’d like to do if selection is new, if selection should be added to the current one and if selection should be subtracted to the current one.
For latest tow cases there are no problem because we have only to set/delete bit on obj, but is selection is new we have to deselect all items before select the new one.
For this topic i would sugest to write a function to deselect all elements and call it before new selection, the function should browse all materials and shaders an delete bit so we can get the function described in this article https://c4dprogramming.wordpress.com/2012/11/26/non-recursive-hierarchy-iteration/

and change it in this way :

GeListNode* GetNextElement(GeListNode* op)
{
  if (!op)
    return NULL;
	// in case of material inspect shaders otherwise continue in usual mode
	if (op->IsInstanceOf(Mmaterial) && ((BaseList2D*)op)->GetFirstShader())
	{
		return ((BaseList2D*)op)->GetFirstShader();
	}
  else if (op->GetDown())
    return op->GetDown();
	while (!op->GetNext() && op->GetUp())
		op = op->GetUp();
	return op->GetNext();
}

as we have multiple object type in the tree (material and shader) we have to call GetFirstShader() in material case of we can traverse all Childs, otherwise if the element is a shader we can traverse in a standard way.

Than we can simply prepare a function that unselect all elements:

void UnselectNodes(BaseDocument *doc)
{
	if (!doc)
		return;
	BaseList2D *mat = (BaseList2D*)doc->GetFirstMaterial();
	if (!mat)
		return;
	while (mat)
	{
		mat->DelBit(BIT_ACTIVE);
		mat = (BaseList2D*)GetNextElement(mat);
	}
}

and finally implement Select():

	virtual void Select(void *root,void *userdata,void *obj,LONG mode)
	{
		if (!root || !obj)
			return;
		BaseList2D *mat = (BaseList2D*)obj;
		BaseDocument *doc = (BaseDocument*)root;
		if (!doc)
			return;
		switch (mode)
		{
			case SELECTION_NEW:
				// unselect all nodes
				UnselectNodes(doc);
				mat->SetBit(BIT_ACTIVE);
				break;
			case SELECTION_ADD:
				mat->SetBit(BIT_ACTIVE);
				break;
			case SELECTION_SUB:
				mat->DelBit(BIT_ACTIVE);
				break;
			default:
				break;
		}

		// update cinema
		EventAdd();
		return;
	}

as you can see passed mode parameter handle selection type so ctrl and shift qualifier will be automatically used by Cinema4D.

Thanks for reading again!

Francesco Guazzi

My passion for computer graphics was born when I was 17. After studying for more then 10 years I worked as a independent 3D Artist for arch viz and product viz.Some years ago I became interested in the world of programming realizing some plugin for Cinema4D for my daily work.Since 2011 I work as a developer for Maxon Computer gmbh.

One Comment

  1. Fantastic!
    I’ve got my tree opening and collapsing properly now.

    The example in the SDK for the tree GUI is insanely complex to try and figure out. This one is much easier on the brain cells.

    Keep up the good work guys,
    -ScottA

Leave a Reply

Your email address will not be published. Required fields are marked *