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 Multiple Selection With LMB

    Cinema 4D SDK
    2
    13
    2.1k
    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.
    • Danchyg1337D
      Danchyg1337
      last edited by m_adam

      Hello! I have a problem with TreeViewFunctions. When i try to select multiple lines with my mouse i get instant crash.
      Here is my Select function:

      void Tree::Select(void* root, void* userdata, void* obj, Int32 mode)
      {
      	TreeNode* res = (TreeNode*)obj;
      	if (!res) return;
      	if (!res->root) {
      		switch (mode)
      		{
      		case SELECTION_NEW:
      			parent->clickID.clear();
      			parent->SelectClick(&(*res->arrayCell));
      			break;
      		case SELECTION_ADD:
      			parent->SelectClick(&(*res->arrayCell));
      			break;
      		case SELECTION_SUB:
      			parent->clickID.erase(std::find(parent->clickID.begin(), parent->clickID.end(), &(*res->arrayCell)));
      			break;
      		}
      	}
      	else {
      		switch (mode)
      		{
      		case SELECTION_NEW:
      			parent->objID.clear();
      			parent->SelectObject(&(*res->cell));
      			break;
      		case SELECTION_ADD:
      			parent->SelectObject(&(*res->cell));
      			break;
      		case SELECTION_SUB:
      			parent->objID.erase(std::find(parent->objID.begin(), parent->objID.end(), &(*res->cell)));
      			break;
      		}
      	}
      }
      

      And Tree class with TreeNode:

      class TreeNode {
      public:
      	Bool root = false, open = false;
      
      	MainMap::iterator cell;				 
      	MainMapCellArray::iterator arrayCell;
      	
      	TreeNode* next = nullptr;
      	TreeNode* pred = nullptr;
      	TreeNode* down = nullptr;
      
      };
      
      class Tree : public TreeViewFunctions {
      public:
      	ManageDialog* parent;
      	MainMap* lst;
      	Int32 currObj;
      	Int32 level = 0;
      	virtual void* GetFirst(void* root, void* userdata);
      	virtual void* GetNext(void* root, void* userdata, void* obj);
      	virtual void* GetDown(void* root, void* userdata, void* obj);
      	virtual void DrawCell(void* root, void* userdata, void* obj, Int32 col, DrawInfo* drawinfo, const GeData& bgColor);
      	virtual Bool IsSelected(void* root, void* userdata, void* obj);
      	virtual Bool IsOpened(void* root, void* userdata, void* obj);
      	virtual String GetName(void* root, void* userdata, void* obj);
      	virtual Int GetId(void* root, void* userdata, void* obj);
      	virtual void* GetPred(void* root, void* userdata, void* obj);
      	virtual void CreateContextMenu(void* root, void* userdata, void* obj, Int32 	lColumn, BaseContainer* bc);
      	virtual Bool ContextMenuCall(void* root, void* userdata, void* obj, Int32 	lColumn, Int32 	lCommand);
      	virtual void Select(void* root, void* userdata, void* obj, Int32 mode);
      	virtual void Open(void* root, void* userdata, void* obj, Bool onoff);
      	virtual void InsertObject(void* root, void* userdata, void* obj, Int32 dragtype, void* dragobject, Int32 insertmode, Bool bCopy);
      	virtual Int32 AcceptDragObject(void* root, void* userdata, void* obj, Int32 dragtype, void* dragobject, Bool& bAllowCopy);
      	virtual Int32 GetDragType(void* root, void* userdata, void* obj);
      	virtual Int32 GetHeaderColumnWidth(void* root, void* userdata, Int32 col, GeUserArea* area);
      	virtual void SetName(void* root, void* userdata, void* obj, const maxon::String& str);
      	virtual Int32 DoubleClick(void* root, void* userdata, void* obj, Int32 	col, MouseInfo* mouseinfo);
      };
      

      Few questions:
      When i select with mouse drag, what does Select get as obj? Is that an array of TreeNodes or TreeView recursively calls Select for each element?

      When i do return; at start of Select i still get crash, i suppose problem can be in something else. What should i do to make it work properly?

      Thanks!

      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by

        Hi @Danchyg1337 on our side everything work nicely,
        Unfortunately with the code provided, I can't help you since I don't have everything. So if you could provide us a simplified version that reproduces the issue I could look at it.

        Just a side note looking at your code doing such a SelectObject(&(*res->cell)); is not very safe. Moreover, you seem to never check for nullptr, so please do it and it should resolve the crash.

        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        Danchyg1337D 1 Reply Last reply Reply Quote 0
        • Danchyg1337D
          Danchyg1337 @m_adam
          last edited by

          @m_adam Thanks for the reply! About SelectObject(&(*res->cell));. Inside of SelectObject() i check for a nullptr at the very start, and i hope this

          TreeNode* res = (TreeNode*)obj;
          if (!res) return;
          

          is enogh to prevent nullptr in Select. Maybe i just misunderstood you about nullptr, correct me then please.

          I'm not sure what should i send you as simpified version, because there is a bunch of code and i don't know which function causes it. I supposed it was Select, but aparently problen is not in it. What about my virtual functions? Could i miss some important virtual function?

          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by

            Yes, but you don't check for arrayCell.

            If you could provide the minimal piece of code that still reproduce your crash, this way I could reproduce the same issue you are facing and can try to help you (help us to help you).
            So at least GetNext/GetFirst/GetDown/IsSelected/GetName/GetID/GetPred/Select methods.

            If you are interested in Treeview you may find valuable information in:

            • TreeView made simple – Part 1[URL-REMOVED].
            • Treeview made simple – Part 2 – Folding & Selections[URL-REMOVED].
            • Using Custom ListView This is python but you may find some valuable information.

            Cheers,
            Maxime.


            [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            Danchyg1337D 1 Reply Last reply Reply Quote 0
            • Danchyg1337D
              Danchyg1337 @m_adam
              last edited by

              @m_adam Here are required functions:

              void* Tree::GetFirst(void* root, void* userdata)
              {
              	TreeNode* obj = (TreeNode*)root;
              	return obj;
              }
              
              void* Tree::GetNext(void* root, void* userdata, void* obj)
              {
              	TreeNode* res = (TreeNode*)obj;
              
              	if (res && res->next)
              		return res->next;
              	return NULL;
              }
              
              void* Tree::GetPred(void* root, void* userdata, void* obj)
              {
              	TreeNode* res = (TreeNode*)obj;
              
              	if (res && res->pred)
              		return res->pred;
              	return NULL;
              }
              
              void* Tree::GetDown(void* root, void* userdata, void* obj)
              {
              	TreeNode* res = (TreeNode*)obj;
              	if (res && res->root && res->down)
              		return res->down;
              
              	return NULL;
              }
              
              String Tree::GetName(void* root, void* userdata, void* obj)
              {
              	if (!obj) return "";
              	TreeNode* res = (TreeNode*)obj;
              	if (res->root) {
              		String name = res->cell->second.GetString(OBJSET_NAME);
              		if (res->cell->second.GetInt32(OBJSET_ID))
              			return name;
              		else {
              			Filename fn(name);
              			return fn.GetFile().GetString();
              		}
              	}
              	return std::get<2>(*(res->arrayCell)).GetString(DESCPREF_NAME);
              }
              
              Int Tree::GetId(void* root, void* userdata, void* obj)
              {
              	return (Int)obj;
              }
              
              Bool Tree::IsSelected(void* root, void* userdata, void* obj)
              {
              	TreeNode* res = (TreeNode*)obj;
              	if (!res) return false;
              	if (res->root) 
              		return std::find(parent->objID.begin(), parent->objID.end(), &(*res->cell)) != parent->objID.end();
              	return std::find(parent->clickID.begin(), parent->clickID.end(), &(*res->arrayCell)) != parent->clickID.end();
              }
              

              And Select() from post above. None of my attempts to check for nullptr helped, so nothing new to provide for that function.
              My code with stuff like &(*res->cell)) and &(*res->arrayCell)) may look unsafe, but it works as intended. If i select with clicking and holding SHIFT button, everything works fine, but when i try to drag LMB i get instant crash even when there are no objects in drag rectangle.

              Thanks!

              1 Reply Last reply Reply Quote 0
              • M
                m_adam
                last edited by m_adam

                "When i select with mouse drag, what does Select get as obj?"
                Normally Select shouldn't be called when you hold a click while dragging.

                Just to clarify and I should have asked first, but how do you select multiple items?

                1. By clicking all of them individually.
                  • Select will be called each time you click.
                2. By clicking on one entry, then SHIFT + click on another one.
                  • For the first click, Select will be called with SELECTION_NEW.
                  • For the second click, Select will be called over each other entry with SELECTION_ADD.
                3. By clicking on one entry, holding the click, drag the mouse down/up, and release the mouse.
                  • I'm not sure I would need to try. But in any case, DragStart will be called to see what to do with the currently selected object and if they are allowed to be dragged. But you can return only TREEVIEW_DRAGSTART_SELECT this way object will be selected (but I'm not sure) or you will need to directly override MouseDown to implement this behavior.

                Note that I overlooked, the fact you didn't override DragStart. Which define what to do when a drag operation is starting, Selection state, and if the object can be dragged.

                But just to recap the dragging process:

                • Drag is starting from the TreeView:
                  • DragStart Called when the Drag operation start, to know what to do. Should return TREEVIEW_DRAGSTART_ALLOW and/or TREEVIEW_DRAGSTART_SELECT to inform what to do.
                  • GetDragType Called after DragStart. Should return the drag type the current cell is representing.
                  • According to the type returned by GetDragType:
                    • DRAGTYPE_ATOMARRAY: GenerateDragArray will be called.
                    • If another drag type is filled then GenerateDragData is called.
                  • SetDragObject is called, so we can store the dragged obj if we want to.
                • Drop received:
                  • AcceptDragObject is called to see if we accept the drag object. obj can be any kind, this is why you need to check the value of dragtype. Should return, if obj should be inserted and where it should be.
                  • InsertObject is the called (if it's an array-like in the case of DRAGTYPE_ATOMARRAY, the function will be called once, with obj* being an AtomArray). It's up to you to do proper insertion within your structure.
                  • A Refresh and a redraw will be done to update the treeview according to the new internal Data Structure.

                In any case I can't really help you more, I still miss, MainMap, MainMapCellArray class InsertObject, AcceptDragObject, GetDragType, DragStart.
                And please provide also the GeDialog or at least the option you used to register the treeview.

                Hope this help a bit more,
                Cheers,
                Maxime.

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                Danchyg1337D 1 Reply Last reply Reply Quote 0
                • Danchyg1337D
                  Danchyg1337 @m_adam
                  last edited by

                  @m_adam I select them with third case, by clicking on one, holding, dragging and releasing mouse.
                  Thanks for useful information, i'm gonna try everything what you suggesed and answer on my results here.

                  1 Reply Last reply Reply Quote 0
                  • Danchyg1337D
                    Danchyg1337
                    last edited by Danchyg1337

                    @m_adam Maybe i'm little misunderstood you. I meant not exactly third case. But a case, when you start holding your mouse at empty space and selecting objects with outcoming rectangle.

                    1 Reply Last reply Reply Quote 1
                    • M
                      m_adam
                      last edited by m_adam

                      Oki doki, I forget this case, so normally what will happen is:

                      1. Select with SELECTION_SUB will be called for each entry.
                      2. Select with SELECTION_NEW will be called for the uppermost entry
                      3. Select with SELECTION_ADD will be called for each bellow entries.

                      So it should work as expected if your methods are safe.
                      Does unselect by ctrl+click on a selected entry work for you? (Looks like SELECTION_NEW, and SELECTION_ADD works so the only left is SELECTION_SUB).

                      Without your code, I can't help you more.

                      Cheers,
                      Maxime.

                      MAXON SDK Specialist

                      Development Blog, MAXON Registered Developer

                      Danchyg1337D 1 Reply Last reply Reply Quote 0
                      • Danchyg1337D
                        Danchyg1337 @m_adam
                        last edited by

                        @m_adam Yes. I just checked that. A select few items with shift+click, and unselect with ctrl+click. Everything works fine.

                        I want to provide you my code, but there is a messy structure like this

                        typedef std::tuple<DescID, Int32, BaseContainer> MainMapCellArrayCell;
                        typedef std::list<MainMapCellArrayCell> MainMapCellArray;
                        typedef std::list<std::pair<MainMapCellArray, BaseContainer>> MainMap;
                        typedef std::pair<MainMapCellArray, BaseContainer> MainMapCell;
                        

                        And i'm not sure it will be efficient way to help. Can i send you whole project to email?

                        1 Reply Last reply Reply Quote 0
                        • M
                          m_adam
                          last edited by

                          Of course, having the whole project would probably more helpful, you can send it to [email protected], and if needed we can sign an NDA.

                          Cheers,
                          Maxime.

                          MAXON SDK Specialist

                          Development Blog, MAXON Registered Developer

                          1 Reply Last reply Reply Quote 0
                          • M
                            m_adam
                            last edited by

                            Hi after digging into your code, the main issue is that your root defined by SetRoot is also your first object.
                            So you should rethink how you structure your internal data structure.
                            I know this is misleading, but root, in the treeviewfunction expects an object that hosts the first object(like the dialog) but the root is not the first object.

                            Note the issue is a more general issue that could lead you to other problems, and in fact, you can reproduce your crash when you click on nothing, the drag operation is fine.
                            Since internally there are several places where the next logic is used

                            DoAction(void* pItem)
                            {
                            (pItem == root)
                               pItem = pItem->GetFirst()
                            else
                               pItem = pItem->GetDown()
                            
                            for (; pItem; pItem = functions->GetNext(root, userdata, pItem))
                                DoAction(pItem);
                            }
                            

                            So in your case, this creates an infinite loop and at the end, create a stack overflow.
                            pItem is the root and the first object. So if you call DoAction with pItem == root,
                            pItem == GetFirst == the previous pItem so in fact we didn't changed the object.
                            Then we arrive in the loop, and the first step is to call DoAction with pItem, so we get the infinite loop.

                            Cheers,
                            Maxime

                            MAXON SDK Specialist

                            Development Blog, MAXON Registered Developer

                            Danchyg1337D 1 Reply Last reply Reply Quote 0
                            • Danchyg1337D
                              Danchyg1337 @m_adam
                              last edited by

                              @m_adam Now i see the problem. Thanks alot for helping!

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