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

    Treeview with custom tree

    SDK Help
    0
    25
    17.0k
    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.
    • H
      Helper
      last edited by

      On 19/01/2014 at 06:24, xxxxxxxx wrote:

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

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 19/01/2014 at 06:28, xxxxxxxx wrote:

        Originally posted by xxxxxxxx

        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

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 19/01/2014 at 10:52, xxxxxxxx wrote:

          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

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 19/01/2014 at 15:47, xxxxxxxx wrote:

            I think this example should set things clear now.

            Best,
            -Niklas

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 19/01/2014 at 17:04, xxxxxxxx wrote:

              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

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 20/01/2014 at 02:51, xxxxxxxx wrote:

                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

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  On 20/01/2014 at 08:23, xxxxxxxx wrote:

                  Originally posted by xxxxxxxx

                  @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.

                  Does the code Niklas provided answer your question though Pim?
                  Because as far as I can see. Even if you create a tree node system from scratch. Which I still don't think you have to do. You still have the problem of how to display text based information in the tree.

                  There's void *parameters in the tree's methods that I think are there to use for things like this. But I keep running into type errors.
                  A good example is the help system in C4D.
                  It's using a tree to display names of pages. And jumps you to them when clicked on in the tree gizmo.
                  How do we do that?

                  That's the kind of example I think Robert was going to show.
                  Are still will to share that code Robert?

                  -ScottA

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    On 20/01/2014 at 09:45, xxxxxxxx wrote:

                    Originally posted by xxxxxxxx

                    Because as far as I can see. Even if you create a tree node system from scratch. Which I still don't think you have to do. You still have the problem of how to display text based information in the tree.

                    Of course you could use the BaseList2D class, but it is not optimized for this purpose. It would come with
                    a huge overhead and you would make your life hard yourself if you want to display any data more than
                    just a name and folding and selection states.

                    Originally posted by xxxxxxxx

                    A good example is the help system in C4D.
                    It's using a tree to display names of pages. And jumps you to them when clicked on in the tree gizmo.

                    How do we do that?

                    It's a Treeview Gui and an HTML Gui. When one item in the tree is clicked, the HTML Gui changes its
                    contents. What is the problem you have here?

                    -Niklas

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      On 20/01/2014 at 10:38, xxxxxxxx wrote:

                      I know how to handle the HTML GUI part.
                      It's making the tree display data other than a BaseObject() or a BaseMaterial() that I can't figure out how to do.

                      For example. This code displays BaseObjects:

                          virtual void *GetFirst(void *root,void *userdata)  
                        {  
                            //Casting from BaseDocument() to root works fine here  
                            BaseDocument *doc = (BaseDocument* )root;   
                        
                            //Grabs the first object in the OM and fills the node data with a BaseObject type of data  
                            return GetActiveDocument()->GetFirstObject();   
                        }  
                      ...  
                        virtual String GetName(void *root, void *userdata, void *obj)  
                        {  
                            //The text in our tree branches comes from the string values in BaseObject  
                            return ((BaseObject* )obj)->GetName();  
                        }
                      

                      Now what if we don't want to display a BaseObject() or BaseMaterial() in our tree?
                      What if for example. We want to use the objects stored in a BaseContainer? Or an array? Or maybe the file names of the HTML pages stored in a folder like the C4D help tree gui works?

                      This does not work:

                        
                        virtual void *GetFirst(void *root,void *userdata)  
                        {  
                            //Casting from BaseContainer() to root does not work like BaseDocument() does  
                            BaseContainer *bc = (BaseContainer* )root;  //<---Ouch! void type error!!  
                          
                            return  bc->GetLong(1)  //<---Trying to return the first object in the container???  
                        }
                      

                      From what I'm guessing.
                      The tree gizmo is a linked list that takes a bunch of items that are stored somewhere (the document, an array, a container, etc...).
                      Using the SDK tree gizmo we can easily use the objects stored in a BaseDocument() to populate the tree. Because somehow...Somewhere. The node for the tree is set up to handle these type of data.

                      But how do we use data from an array, or a BaseContainer, or a folder containing a bunch of HTML files like the C4D help tree gizmo works?
                      Making a custom node only shows how to set up the way a tree looks and behaves. But it doesn't address how to use it with data stored someplace other than BaseDocument or BaseList2D.

                      I had thought that we could maybe use the void *params. to do that. But maybe not?

                      -ScottA

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        On 20/01/2014 at 11:27, xxxxxxxx wrote:

                        That is the very reason to use your own Node class, Scott. First, you build the tree, then you display it
                        in the TreeViewCustomGui. The TreeViewCustomGui is nowhere fixed to be used with objects from a
                        document, it is just a coincidence (intentionally of course) that it is easy to use with them.

                        Regarding your pseudo-example code, GetFirst(), just as many of the other methods in the
                        TreeViewFunctions class, returns a void* pointer. It is up to you to be sure about the actual type of
                        the memory address to return, all it wants is a memory address. From this memory address, it will
                        ask you for more data (GetNext(), GetName(), IsSelected() all get that void pointer passed as an argument).

                        In your example, you will probably want to do something like this instead?

                        virtual void* GetFirst(void* root, void* ud) {
                            BaseDocument* doc = GetActiveDocument();
                            if (doc)
                                return doc->GetFirstObject();
                            return nullptr;
                        }
                          
                        // ... GetNext(), GetPred(), GetDown(), GetUp() ...
                          
                        virtual String GetName(void* root, void* ud, void* obj) {
                            BaseObject* op = static_cast<BaseObject*>(obj);
                            BaseContainer* bc = op->GetDataInstance();
                            if (bc)
                                return bc->GetString(1);
                            return "<???>";
                        }
                        

                        Best,
                        -Niklas

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          On 20/01/2014 at 12:36, xxxxxxxx wrote:

                          Ok Thanks.
                          That answers the question I had whether or not we must create a new Node or not.

                          But I still don't know how to write the code that declares what data type the node can hold and use.
                          So that I can display a list of filenames in the tree rather than object names?

                          You've got three classes involved in your tree gizmo
                          -class BaseNode
                          -class Node : public BaseNode
                          -class TreeModel : public TreeViewFunctions

                          I assume we don't declare the data type in the TreeModel class.
                          So where (how) do we declare the data type we want to display in our tree. If I wanted to display the names of HTML files I have in a folder on my HD?

                          -ScottA

                          *Edit-- Oh wait..I think I can do that in the AddNode() function. And use Filename() there to set the node's text instead of "NewNode".

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