Plugin Cafe Homepage
Forum Home Forum Home > Plugin Cafe > SDK Help
  New Posts New Posts
  FAQ FAQ  Forum Search

Treeview with custom tree

Page  12>
Author
Message
  Topic Search Topic Search
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Topic: Treeview with custom tree
    Posted: 2014 Jan 16 at 7:39am

User Information:

Cinema 4D Version:   R15 
Platform:   Windows  ;   
Language(s):     C++  ;   

---------

All the Treeview examples I see are using cinema objects or materials.
What if I want to create my own tree and display this tree using treeview.

E.g.:

- Family A
        - parent A
        - parent B
- Family B
        - ...

Etc.

I think I have to make the tree using BaseList2D hierarchies (which include GeListNode and GeListHead), but I do not if this is the correct way and if so, where to start?

 
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2014 Jan 16 at 8:25am
You need to create your own datastructure that can represent a tree. Like

class Node {
public:

    Node* next, pred, up, down;

    Node() : next(nullptr), pred(nullptr), up(nullptr), down(nullptr) { }

    ~Node() {
        next = pred = up = down = nullptr;
    }

    // ...

};

The gui/activeobject.cpp example in the Cinema 4D SDK does it.

Best,
-Niklas


Edited by NiklasR - 2014 Jan 16 at 8:26am
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 16 at 10:54am
Thank you.
I'll try it out.
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 17 at 12:14am
Sorry, more complicated then I thought.
From the example should I use DebugArray or ListHeader / ListObject?
A more simple example would help me.
If a simple example on how to create tree data structure.

My C++ is still not that good.
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2014 Jan 17 at 12:23am
Hi,

See this simple example in C4D Programming Blog:

MAXON
Developer Support
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 17 at 12:29am
Hi Yannick,

I know that example and that was the start of my question.
Instead of using objects or materials I want to use my own tree and display it with treeview.
But I do not know how to create my own tree data structure and display it / link it to treeview.

Regards, Pim
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2014 Jan 17 at 12:51am
Hey Yannick,

I think that's not what Pim is searching for. The blog shows how to use the TreeView by leveraging the
BaseList2D's hierarchical tree structure, which is what he already denoted as being the only thing he
examples for.

Pim,

it has less to do with C++ but with logic. 

class Node {
public:

    Node* next;
    Node* pred;
    Node* up;
    Node* down;

    Node() : next(nullptr), pred(nullptr), up(nullptr), down(nullptr) { }

    Node(const String& name) : Node() {
        this->name = name;
    }

    virtual ~Node() {
        next = pred = up = down = nullptr;
    }

    void InsertAfter(Node* pred) {
        if (pred->next) {
            pred->next->pred = this;
            this->next = pred->next;
        }
        pred->next = this;
        this->pred = pred;

        if (pred->up)
            this->up = pred->up;
    }

    void InsertBefore(Node* next) {
        if (next->pred) {
            next->pred->next = this;
            this->pred = next->pred;
        }
        next->pred = this;
        this->next = next;

        if (pred->up)
            this->up = pred->up;
        if (this->pred == nullptr && this->up)
            this->up->down = this;
    }

    void InsertUnder(Node* up) {
        if (up->down) {
            this->InsertBefore(up->down);
        }
        else {
            up->down = this;
            this->up = up;
        }
    }

    void Remove() {
        if (this->up) {
            if (this->pred == nullptr)
                assert(this->up->down == this);
            if (this->up->down == this) {
                this->up->down = this->next;
            }
        }
        if (this->next) {
            this->next->pred = this->pred;
        }
        if (this->pred) {
            this->pred->next = this->next;
        }

        this->up = this->pred = this->next = nullptr;
    }

};

Add some members to this class which will contain data that you want to display in the Treeview and
use it to cast void* to Node* instead of BaseList2D*. Beware of the memory management, while the
BaseList2D* classes are managed by Cinema 4D (assuming they are inserted in the tree), you'll need
to manage the memory of the Node* structure for yourself.

Best,
-Niklas



Edited by NiklasR - 2014 Jan 17 at 2:50am
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 17 at 2:31am
Ok, based on your example and another tutorial, I can now create my own tree structure.
What I need to do now is:
SetRoot(void* root, TreeViewFunctions* functions, void* userdata)
void Refresh()

Thus override in TreeViewFunctions at least GetFirstGetDownGetNext and GetPred.
How do I handle child's in my structure. Set an extra bit to indicate?

void* root is the address of the first node?
In my case 'start'? See code below.

struct node
{
    String name;
    node *next;
    node *prev;
};
void addnode();
void display();
node *start=NULL, *temp1, *temp2, *temp3;

void display()        //display all Nodes
{
    temp3=start;
    if(start==NULL)
        //cout<<"no node to display"<<endl;
GePrint ("Nothing in list.");
    else
    {
      while(temp3->next!=NULL)
      {
//cout<<"Data stored is "<<temp3->data<<" at "<<temp3<<endl;
GePrint ("naam: " + temp3->name);
         temp3=temp3->next;
      }
      //cout<<"Data stored is "<<temp3->data<<" at "<<temp3<<endl;
 GePrint ("naam: " + temp3->name);
    }
}

void addnode(String naam)          //adding node at end
{
    temp1=new node;
temp1->name = naam;

if(start==NULL) //first?
    {
        start=temp1;
        temp1->next=NULL;
        temp1->prev=NULL;
    }
    else
    {
        temp2=start;
        while(temp2->next!=NULL)
            temp2=temp2->next;
        temp2->next=temp1;
        temp1->prev=temp2;
        temp1->next=NULL;
    }  
}
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2014 Jan 17 at 2:55am
A bit? No. As you can clearly see in my above code, a Node has not also a "prev" and "next" pointer,
but also a "child" and "up" pointer. You could use the class directly, I wrote it extra for this very answer
and tested it. Wink

You can either alter the code or subclass it. Something like this (though this code is untested):

class MyNode : public Node {

    typedef Node super;

public:

    String name;
    Bool selected, folded;

    MyNode(const String& name) : super(), name(name), selected(false), folded(true) { }

};

class MyTreeViewFunctions : public TreeViewFunctions {

public:

    virtual void* GetFirst(void* root, void* ud) {
        if (root)
            return static_cast<MyNode*>(root)->down;
        return nullptr;
    }

    virtual String GetName(void* root, void* ud, void* obj) {
        if (obj)
            return static_cast<MyNode*>(obj)->name;
        return "???";
    }

    // ...


};

class MyDialog : public GeDialog {

    MyNode m_rootNode;
    TreeViewCustomGui* m_tree;
    MyTreeViewFunctions m_model;

public:

    // ...

    virtual Bool InitValues() {
        
        m_tree->SetRoot(&m_rootNode, &m_model, nullptr);
        return true;
    }

};
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 17 at 3:39am
It is getting clearer and clearer, thanks.

I looked at your first code, but did not use it because:
- I got errors on   
    Node(const String& name) : Node() {
        this->name = name;
    }

"Node" is not a nonstatic data member or base class of class "Node".
Class "Node" has no member "name"

- I do not how to insert the first record.

So, how to use your class Node to insert records?

Thanks for your effort, Pim
Back to Top
ScottA View Drop Down
Member
Member


Joined: 2011 Jan 07
Online Status: Offline
Posts: 2288
Direct Link To This Post Posted: 2014 Jan 17 at 12:52pm
Originally posted by pgrooff

All the Treeview examples I see are using cinema objects or materials.
What if I want to create my own tree and display this tree using treeview.

E.g.:

- Family A
        - parent A
        - parent B
- Family B
        - ...


Is there a reason that you're writing your own node?
The code you're posting (GetFirst, GetNext, GetUp, etc..) is already set up for us in the TreeViewFunctions SDK class.

You haven't said what you want to display in your tree gizmo.
But if you don't want to display objects or materials in your tree. And want to use it for displaying something else. Then I don't see why you'd go through the extra trouble of writing a node class.

Suppose you wanted to display tags in the tree gizmo.
Instead of using:
        virtual void *GetFirst(void *root,void *userdata)
        {
            if (!root) return NULL;
            BaseDocument *doc = (BaseDocument*)root;
            return (BaseList2D*)doc->GetFirstMaterial();
        }


You would use something like this instead
        virtual void *GetFirst(void *root,void *userdata)
        {
            if (!root) return NULL;
            BaseDocument *doc = (BaseDocument*)root;
            return GetActiveDocument()->GetFirstObject()->GetFirstTag();
        }



Are you sure you really need to go through the trouble of creating a custom Node?

-ScottA


Edited by ScottA - 2014 Jan 17 at 12:54pm
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2014 Jan 17 at 1:43pm
@Scott: Well, he said he's only seen examples of using BaseList2D elements with the TreeView. I suppose
he wants to display something very different. And it's not that much of a trouble to write your own
node class, the code is just a few posts earlier.

@Pim: I used a C++11 feature in the code. You can rewrite the Node constructor like

    Node() : next(nullptr), pred(nullptr), up(nullptr), down(nullptr), name("") { }

    Node(const String& name) : next(nullptr), pred(nullptr), up(nullptr), down(nullptr), name(name) {
    }

instead of calling Node() in the initializer list of the second constructor.

What problems do you have about "inserting the first record"? Just use the Insert~() functions

Node* root = new Node("ROOT");
Node* firstChild = new Node("First Entry");
firstChild->InsertUnder(root);

-Niklas


Edited by NiklasR - 2014 Jan 17 at 1:43pm
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 19 at 3:32am
@Scott: I want to download information from the Internet and present it in a treeview.
So, the information is not cinema 4d info, thus I need to define my own nodes and present it in the treeview.

@Niklas: More clear now. Thanks for the example.
Quite possibility I will come back with more questions.

Regards, Pim
Back to Top
kuroyume0161 View Drop Down
Member
Member
Avatar

Joined: 2002 Oct 29
Location: United States
Online Status: Offline
Posts: 3665
Direct Link To This Post Posted: 2014 Jan 19 at 6:18am
It is quite possible (and easy) to use your own class structures in a tree view.  InterPoser Pro uses a folder/file system to display items as a tree view.  You need to have a root and items and associate levels with them (parents/children).  If you would like to see my code, PM me.
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 19 at 6:24am
Hi Niklas, sorry still very confusing.
I can now create a data structure and defined the TreeViewFunctions.
Not all of them, GetFirst and GetName, GetDown, GetNext and SetName.

I now get a window with a starting treeview and one little arrow.
No names however.

Is *root in my case always root (so I have to define it globally) or is it somewhere else defined?
Also, I seem to be mixing up class Node and class MyNode.
When to use which class?

#include "c4d.h"
 
#define ID_SHADER_BROWSER 11229405 // plugin id test

class Node {
public:

    Node* next;
    Node* pred;
    Node* up;
    Node* down;
String name;

Node() : next(nullptr), pred(nullptr), up(nullptr), down(nullptr), name("") { }

    Node(const String& name) : next(nullptr), pred(nullptr), up(nullptr), down(nullptr), name(name) {
    }

    virtual ~Node() {
        next = pred = up = down = nullptr;
    }

    void InsertAfter(Node* pred) {
        if (pred->next) {
            pred->next->pred = this;
            this->next = pred->next;
        }
        pred->next = this;
        this->pred = pred;

        if (pred->up)
            this->up = pred->up;
    }

    void InsertBefore(Node* next) {
        if (next->pred) {
            next->pred->next = this;
            this->pred = next->pred;
        }
        next->pred = this;
        this->next = next;

        if (pred->up)
            this->up = pred->up;
        if (this->pred == nullptr && this->up)
            this->up->down = this;
    }

    void InsertUnder(Node* up) {
        if (up->down) {
            this->InsertBefore(up->down);
        }
        else {
            up->down = this;
            this->up = up;
        }
    }

    //void Remove() { code removed

};

class MyNode : public Node 
{
    typedef Node super;

public:

    String name;
    Bool selected, folded;

    MyNode(const String& name) : super(), name(name), selected(false), folded(true) { }

};

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();
}

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);
    }
}

//----------------------------------------------------------------------------------------
///TreeView Functions Table class
//----------------------------------------------------------------------------------------
class CustomTree : public TreeViewFunctions
{
public:
 

virtual void* GetFirst(void* root, void* ud) {
        if (root)
{
            return static_cast<MyNode*>(root)->down;
}
        return nullptr;
    }

    virtual String GetName(void* root, void* ud, void* obj) {
        if (obj)
{
            return static_cast<MyNode*>(obj)->name;
}
        return "???";
    }

    // ...

virtual Bool IsOpened(void *root,void *userdata,void *obj)
{
return  false;
}

virtual void Open(void* root, void* userdata, void* obj, Bool onoff)
{
return;
}

void SetName(void* root, void* userdata, void* obj, const String& str)
{
static_cast<MyNode*>(obj)->name = str;
return;
}

    virtual void* GetDown(void *root,void *userdata,void *obj)
    {
        return static_cast<MyNode*>(root)->down;
    }
 
    virtual void*   GetNext(void *root,void *userdata,void *obj)
    {
        return static_cast<MyNode*>(root)->next;
    }
 
virtual void Select(void *root,void *userdata,void *obj,Int32 mode)
{
return;
}

virtual Bool IsSelected(void *root,void *userdata,void *obj)
{
return FALSE;
}
 
    virtual Int   GetId(void *root,void *userdata,void *obj)
    {
        return (Int)obj;
    }
 
    virtual Int32 GetDragType(void *root,void *userdata,void *obj)
    {
        return NOTOK;
    }
};

//----------------------------------------------------------------------------------------
///Dialog class
//----------------------------------------------------------------------------------------
class CustomBrowser : public GeDialog
{
public:
 
    CustomBrowser()
    {
        _customTreeGui = NULL;
    }
 
    ~CustomBrowser()
    {
    }
 
    // define dialog with some gadget build menus, title and so on
    virtual Bool CreateLayout(void)
    {
        // call class parent function
        Bool res = GeDialog::CreateLayout();
        // set dialog title
        SetTitle("Custom Tree Browser");
 
//attach treeview so the GUI element and define its parameter via treedata BaseContainer
BaseContainer treedata;
treedata.SetBool(TREEVIEW_ALTERNATE_BG,TRUE);
_customTreeGui = (TreeViewCustomGui*)AddCustomGui(1000, CUSTOMGUI_TREEVIEW, String(), BFH_SCALEFIT|BFV_SCALEFIT, 0, 0, treedata);

        if (_customTreeGui)
        {
            BaseContainer layout;
            // add column 0 with type LV_TREE so default tree with name
            layout.SetInt32(0, LV_TREE);
            // set column layout to the GUI element
            if (!_customTreeGui->SetLayout(1,layout))
                return FALSE;
        }
 
        return res;
    }
 
    // set tree root and update it
    Bool UpdateTree(Node* root)
    {
        if (!_customTreeGui->SetRoot(root, &_customTree, NULL))
            return FALSE;
        // update tree GUI
        _customTreeGui->Refresh();
        return TRUE;
    }
 
    // initialize dialog
    Bool InitValues(void)
    {
        // call parent function
        if (!GeDialog::InitValues())
            return FALSE;

// build tree datastructure
Node* root = new Node("ROOT");
Node* firstChild = new Node("First Entry");
firstChild->InsertUnder(root);
Node* secondChild = new Node("Second Entry");
secondChild->InsertUnder(firstChild);
Node* thirdChild = new Node("Third Entry");
thirdChild->InsertUnder(secondChild);

        // build tree and set root
        if (!UpdateTree(root))
            return FALSE;
        return TRUE;
    }
 
    // react toglobal notifications
    Bool CoreMessage(Int32 id, const BaseContainer& msg)
    {
        //if (id == EVMSG_CHANGE)
        //{
            // get active document
        //    BaseDocument * doc = GetActiveDocument();
        //    if (!doc)
        //        return FALSE;
            // build tree and set root How to get root?
        //    if (!UpdateTree(doc))
        //        return FALSE;
        //}
        return GeDialog::CoreMessage(id, msg);
    }
 
private:
    CustomTree                  _customTree;
    TreeViewCustomGui       *_customTreeGui;
};

...




 

Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 19 at 6:28am
Originally posted by kuroyume0161

It is quite possible (and easy) to use your own class structures in a tree view.  InterPoser Pro uses a folder/file system to display items as a tree view.  You need to have a root and items and associate levels with them (parents/children).  If you would like to see my code, PM me.

Hi Robert, your inbox is full.
I like to see your code very much.

Regards, Pim
Back to Top
ScottA View Drop Down
Member
Member


Joined: 2011 Jan 07
Online Status: Offline
Posts: 2288
Direct Link To This Post Posted: 2014 Jan 19 at 10:52am
I'd like to see an example too Robert.
Because while I can also wrote the gizmo. It crashes when I try to add things to it due to using a custom Node.

And I still don't understand why we need to write a completely new Node for this?
If we don't want to use a  baselist2D object. Can't we just substitute another type of object in the various TreeViewFunctions class methods?

Seeing a working example might help me answer those questions.

-ScottA
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2014 Jan 19 at 3:47pm
I think this example should set things clear now.



Best,
-Niklas


Edited by NiklasR - 2014 Jan 19 at 3:48pm
Back to Top
ScottA View Drop Down
Member
Member


Joined: 2011 Jan 07
Online Status: Offline
Posts: 2288
Direct Link To This Post Posted: 2014 Jan 19 at 5:04pm
Thanks Niklas,

Only you're using a bunch of C++11 stuff.
And when I convert it to work in R13. The selections don't work. And the new items are never created as children.

I'm still trying to figure out why they don't work.

-ScottA
Back to Top
pgrooff View Drop Down
Member
Member


Joined: 2010 Nov 05
Online Status: Offline
Posts: 817
Direct Link To This Post Posted: 2014 Jan 20 at 2:51am
Great, thanks Niklas.
For me with R15 and VS Express 2010 it is working great.
Adding new nodes, drag&drop, rename, delete, everything works!
It looks very advanced C++ programming, so I will learn a lot from it.

Thanks, Pim
Back to Top
Page  12>

Forum Jump Forum Permissions View Drop Down

Bulletin Board Software by Web Wiz Forums® version 9.61 [Free Express Edition]
Copyright ©2001-2009 Web Wiz

This page was generated in 0.125 seconds.