TreeGUI CheckBoxes



  • On 11/09/2013 at 14:18, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   13 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    I'm having some trouble with the checkbox option for the tree gui.
    After I add it using layout.SetLong('chck', LV_CHECKBOX);. It's greyed out and I can't click on it!?
    Is there some flag that has to be set to allow the user change it?

    BTW:
    I do know how to change it depending on the type of object that's in the same cell. By using the overriden IsChecked() function. Like this:

        virtual LONG IsChecked(void* root, void* userdata, void* obj, LONG lColumn)  
      {  
          BaseDocument *doc = GetActiveDocument();  
          BaseObject *op = (BaseObject* )obj;  
      
          if (op->IsInstanceOf(Opolygon))  
          {  
              return LV_CHECKBOX_ENABLED|LV_CHECKBOX_CHECKED;  
          }  
          else return FALSE;  
      }
    

    But this is not the desired way I need to use it.
    I would like to be able to click this checkbox myself.

    -ScottA



  • On 12/09/2013 at 05:38, xxxxxxxx wrote:

    Hi Scott,

    1. You need to store the state of the checkbox somewhere, where you can set and unset it.
    2. Override SetCheck() to perform the setting/unsetting

        virtual LONG IsChecked(void\* root, void\* ud, void\* obj, LONG column) {
            BaseObject\* op = static_cast<BaseObject\*>(obj);
            if (!op) return 0;
    
            LONG state = LV_CHECKBOX_ENABLED;
            switch (column) {
                case COLUMN_SELECTED:
                    if (op->GetBit(BIT_ACTIVE)) {
                        state |= LV_CHECKBOX_CHECKED;
                    }
                    break;
                default:
                    break;
            }
    
            return state;
        }
    
        virtual void SetCheck(void\* root, void\* ud, void\* obj, LONG col, Bool value, const BaseContainer& msg) {
            BaseObject\* op = static_cast<BaseObject\*>(obj);
            if (!op) return;
    
            switch (column) {
                case COLUMN_SELECTED:
                    if (value) op->SetBit(BIT_ACTIVE);
                    else op->DelBit(BIT_ACTIVE);
                    break;
                default:
                    break;
            }
        }
    

    Best,
    -Niklas



  • On 12/09/2013 at 08:03, xxxxxxxx wrote:

    Hi Nik,
    Thanks for the help.

    What are these?:

    COLUMN_SELECTED
    
    
    value
    

    The problem is I don't really understand the the logic of how these function pairs work:
    SetCheck-ISChecked
    Select-IsSelcted
    Open-IsOpened
    etc...

    I've spent hours trying to understand what the heck this programmer was thinking. But I just can't figure out what he was thinking when he created this nightmare.

    Using simple logic. You would logically think that we would use the Set,Select,Open functions to tell C4D to do something.
    And you would logically think that the IsSet,IsSelected, IsOpen functions would act like receivers of those other functions. And react to them.
    But no.........It's not working like that. And I can't for the life of me figure out what the heck the guy who wrote this thing was thinking.

    -ScottA



  • On 12/09/2013 at 09:46, xxxxxxxx wrote:

    This is what a logical person would expect.

    IsChecked() is in charge of physically setting the checkbox to: enabled, checked, or neither
    SetChecked() is the method that tells IsChecked what to do
    So logically. You would get the current value of IsChecked() inside SetChecked(). And then make a decision what to do. And then tell IsChecked() what to do.
    But there is no way I can find to make SetChecked() communicate with IsChecked()

    In addition that.
    If IsChecked() is not set to LV_CHECKBOX_ENABLED. Then you cannot use SetChecked() at all!?

    Example of logic that does not work:

    Bool mytoggle;

    virtual LONG IsChecked(void* root, void* userdata, void* obj, LONG lColumn)  //Note: lColumn returns the LV_CHECKBOX ID
      {
          return TRUE;
      }

    virtual void SetCheck(void* root, void* userdata, void* obj, LONG lColumn, Bool bCheck, const BaseContainer &bcMsg)
      {
          //This method only returns a value if IsChecked() is set to LV_CHECKBOX_ENABLED

    bCheck = IsChecked(root, userdata, obj, lColumn);  //Gets the bit value returned from IsChecked()

    mytoggle = !mytoogle
          
          if(mytoggle == TRUE)
              bCheck = LV_CHECKBOX_ENABLED         //Make IsChecked() enable the checkbox

    else
              bCheck = 0                           //Make IsChecked return 0 (not checked or enabled)
         
      }

    The logic for this thing makes absolutely no sense at all to me.
    This is why things need to documented properly, and clearly.

    -ScottA



  • On 12/09/2013 at 10:12, xxxxxxxx wrote:

    Got it!
    Sorry for the multiple posts. But this thing has been a nightmare for me.
    You can't image the amount of swearing I've been doing for that past two days about this thing.🙂

    Here's the code I came up with that lets the user toggle the checkbox in the tree gui:

    class MyTree : public TreeViewFunctions  
    {  
      Bool mytoggle;   //Used to toggle the checkbox on/off  
      
      public:  
      
      //The various tree methods here...  
      
      
      virtual LONG IsChecked(void* root, void* userdata, void* obj, LONG lColumn)  //lColumn returns the LV_CHECKBOX ID  
      {  
          LONG state = LV_CHECKBOX_ENABLED;  
          if (mytoggle)   
          {  
              state |= LV_CHECKBOX_CHECKED;  
          }  
          return state;  
      }  
      
      virtual void SetCheck(void* root, void* userdata, void* obj, LONG lColumn, Bool bCheck, const BaseContainer &bcMsg)  
      {  
          mytoggle = !mytoggle;    //Toggles the switch value from FALSE/TRUE each time clicked  
      }  
      
      
      //The other tree methods...  
      
    };
    

    -ScottA



  • On 12/09/2013 at 10:37, xxxxxxxx wrote:

    Dammit! Don't got it.
    That enables all the check boxes for all the tree items!

    Why is this so difficult to do?
    All I want to do is be able select a checkbox on each individual tree item. And when I do that, it makes the object in the same cell active.
    Why is this such a huge massive thing to accomplish?

    -ScottA



  • On 13/09/2013 at 13:33, xxxxxxxx wrote:

    Hi Scott,

    Originally posted by xxxxxxxx

    What are these?:  
    COLUMN_SELECTED
    value

    You can have multiple columns in a TreeViewCustomGui, these columns are specified by passing
    a BaseContainer to TreeViewCustomGui::SetLayout(). The column parameter represents the column
    the TreeView polls you for data. COLUMN_SELECTED served as an example, being a symbol for a
    column of the TreeView. You need it to distinguish between the columns in the tree-view. Imagine
    you have two columns in your tree-view that have a checkbox!

    Originally posted by xxxxxxxx

    The problem is I don't really understand the the logic of how these function pairs work:
    SetCheck-ISChecked
    Select-IsSelcted
    Open-IsOpened
    etc...

    The documentation is quite concise about this. The TreeViewCustomGui polls your TreeViewFunctions
    object for information. It asks "is the element selected?", "is the element opened (unfolded)?",
    "is the checkbox checked?", etc.

    Furthermore, it tells you "an element got selected/deselected", "an element got folded/unfolded",
    "a checkbox was checked/unchecked", etc.

    Originally posted by xxxxxxxx

    Using simple logic. You would logically think that we would use the Set,Select,Open functions to tell C4D to do something.
    And you would logically think that the IsSet,IsSelected, IsOpen functions would act like receivers of those other functions. And react to them.

    No, in this case you are the one implementing that functionality. You must see it from the
    perspective of the TreeViewCustomGui. Eg. if you were writing the TreeView, and you receive a
    mouse event and come to the conclusion that an element was selected by the user, you'd do

       void* element = // some element returned by TreeViewFunctions::GetNext()/GetDown()/etc.
        // ...
        if (element_got_selected) {
            my_tree_view_functions.Select(root, ud, element, SELECTION_ADD);
        }
    

    Originally posted by xxxxxxxx

    Bool mytoggle;

    virtual LONG IsChecked(void* root, void* userdata, void* obj, LONG lColumn)  //Note: lColumn returns the LV_CHECKBOX ID
        {
            return TRUE;
        }

    virtual void SetCheck(void* root, void* userdata, void* obj, LONG lColumn, Bool bCheck, const BaseContainer &bcMsg)
        {
            //This method only returns a value if IsChecked() is set to LV_CHECKBOX_ENABLED

    bCheck = IsChecked(root, userdata, obj, lColumn);  //Gets the bit value returned from IsChecked()

    mytoggle = !mytoogle
            
            if(mytoggle == TRUE) 
                bCheck = LV_CHECKBOX_ENABLED         //Make IsChecked() enable the checkbox

    else
                bCheck = 0                           //Make IsChecked return 0 (not checked or enabled)
            
        }

    The logic for this thing makes absolutely no sense at all to me.
    This is why things need to documented properly, and clearly.

    It is not the documentation that is lacking of information here. You are missing an important point.
    You have one variable for all the elements in your tree-view. The change to that variable will affect
    all checkboxes. Simple as that.

    You usually store the data in the object that you use to represent your tree structure, the obj
    parameter. In the example from my previous post, I demonstrated implementing the IsChecked()
    method assuming you are representing a structure of BaseObjects.

    This is a quite complete example. The whole thing is straight forward to implement. I have 
    written it from scratch and did not try to compile it, but it should serve its purpose of
    demonstration very well.

    >
    > class MyTreeViewFunctions : public TreeViewFunctions {
    >
    > public:
    >
    > virtual void* GetFirst(void* root, void* ud) {
    > BaseDocument* doc = GetActiveDocument();
    > if (doc) return doc->GetFirstObject();
    > else return NULL;
    > }
    >
    > virtual void* GetNext(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetNext();
    > else return NULL;
    > }
    >
    > virtual void* GetPred(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetPred();
    > else return NULL;
    > }
    >
    > virtual void* GetDown(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetDown();
    > else return NULL;
    > }
    >
    > virtual void* GetUp(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetUp();
    > else return NULL;
    > }
    >
    > virtual Bool IsSelected(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetBit(BIT_ACTIVE);
    > else return FALSE;
    > }
    >
    > virtual void Select(void* root, void* ud, void* obj, LONG mode) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > BaseDocument* doc = GetActiveDocument();
    >
    > switch (mode) {
    > case SELECTION_NEW:
    > if (doc) doc->SetActiveObject(NULL);
    > case SELECTION_ADD:
    > if (op) op->SetBit(BIT_ACTIVE);
    > break;
    > case SELECTION_SUB:
    > if (op) op->DelBit(BIT_ACTIVE);
    > break;
    > }
    > }
    >
    > virtual Bool IsOpened(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetBit(BIT_OFOLD);
    > else return FALSE;
    > }
    >
    > virtual void Open(void* root, void* ud, void* obj, Bool mode) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) {
    > if (mode) op->SetBit(BIT_OFOLD);
    > else op->DelBit(BIT_OFOLD);
    > }
    > }
    >
    > virtual String GetName(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetName();
    > else return "???";
    > }
    >
    > virtual Bool IsChecked(void* root, void* ud, void* obj, LONG column) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    >
    > LONG state = 0;
    > switch (column) {
    > case COLUMN_HIDDEN:
    > if (op) {
    > state |= LV_CHECKBOX_ENABLED;
    >
    > // If the object is hidden in the Object Manager, the checkbox should
    > // be checked.
    > if (op->GetNBit(NBIT_OHIDE)) {
    > state |= LV_CHECKBOX_CHECKED;
    > }
    > }
    > break;
    > }
    >
    > return state;
    > }
    >
    > virtual void SetCheck(void* root, void* ud, void* obj, LONG column, Bool chcked, const BaseContainer& msg) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    >
    > switch (column) {
    > case COLUMN_SELECTED:
    > if (op) {
    > // Hide the object if the checkbox was checked, unhide if it was unchecked.
    > NBITCONTROL control;
    > if (checked) control = NBITCONTROL_SET;
    > else control = NBITCONTROL_CLEAR;
    > op->SetNBit(NBIT_OHIDE, control);
    > }
    > }
    > }
    >
    > }
    >
    > class MyDialog : public GeDialog {
    >
    > TreeViewCustomGui* m_tree;
    > MyTreeViewFunctions m_functions;
    >
    > public:
    >
    > virtual Bool CreateLayout() {
    > // Create a TreeViewCustomGui here
    > }
    >
    > virtual Bool InitValues() {
    > if (m_tree) {
    > BaseContainer layout;
    > layout.SetLong(COLUMN_NAME, LV_TREE);
    > layout.SetLong(COLUMN_HIDDEN, LV_CHECKBOX);
    > m_tree->SetLayout(2, layout);
    > m_tree->SetHeaderText(COLUMN_NAME, "Name");
    > m_tree->SetHeaderText(COLUMN_HIDDEN, "Hidden");
    > m_tree->SetRoot(NULL, &m_functions, NULL);
    > m_tree->Refresh();
    > }
    >
    > return TRUE;
    > }
    >
    > };

    Originally posted by xxxxxxxx

    Why is this so difficult to do?
    All I want to do is be able select a checkbox on each individual tree item. And when I do that, it makes the object in the same cell active.
    Why is this such a huge massive thing to accomplish?

    See above, you can't store ONE value for MANY objects and expect it to work like that. (Maybe
    looking at some data-relationship models might help you too).

    Best regards,
    -Niklas



  • On 13/09/2013 at 14:42, xxxxxxxx wrote:

    Thanks for the effort Nik.
    But I'm afraid that's not helping me figure out my checkbox problem.
    I've learned a bit more about the tree gui over the past few days. And I think I've got a better understanding about how the two function pairs work together now.
    I also understand how the columns work too.

    But the thing I need (and don't understand) is how to make the checkboxes checkable by the user. So the user can select them individually.
    I can check them all. But not individually.

    You're checkbox code has problems in it that I don't understand how to fix:
    COLUMN_HIDDEN:    is Undefined      <--- which .h file is this ID found?
    COLUMN_SELECTED:  is Undefined      <--- which .h file is this ID found?
    op->SetNBit(NBIT_OHIDE, control);   <--- BaseObject has no such member?

    Again. Just to clarify.
    I just need to know how to make those checkboxes checkable. So the user can physically click on them individually. __So the user can click on them to turn them on/off.
    That's all. No selecting, or hiding of anything in the tree, or the scene.
    Your code for that has things in it that don't work for me. And I can't figure out how to change it so  it works.

    Thanks for the help,
    -ScottA

    *Edit- I don't know if this helps or not.
    But I think what I'm looking for is the BIT code for the SetCheck() & IsChecked() functions so that The user can physically toggle the individual checkboxes on/off.



  • On 13/09/2013 at 15:20, xxxxxxxx wrote:

    As I said before, COLUMN_SELECTED and COLUMN_HIDDEN are just some random symbols that should
    identify your columns. Define them somewhere, whatever value you feel like.

    Oops, you are right about the SetNBit() method, it is actually called ChangeNBit().

    Originally posted by xxxxxxxx

    I just need to know how to make those checkboxes checkable. So the user can physically click on them individually. So the user can click on them to turn them on/off. That's all. No selecting, or hiding of anything in the tree, or the scene.

    Again Scott, you are using one single variable to save the state of many different checkboxes. But
    what you need to do, is to store a variable per element. And this is usually done by storing the value
    in the element itself (which is obj in IsChecked() or SetChecked()). A BaseObject has many different
    bits and I just used one of them to represent the state of the checkbox.

    Edit.
    And COLUMN_SELECTED is supposed to be COLUMN_HIDDEN. Guess its better I write a complete
    example



  • On 13/09/2013 at 15:35, xxxxxxxx wrote:

    Thiis should compile fine now

    >
    > #include <c4d.h>
    >
    > #define COLUMN_NAME 100
    > #define COLUMN_HIDDEN 101
    >
    > class MyTreeViewFunctions : public TreeViewFunctions {
    >
    > public:
    >
    > virtual void* GetFirst(void* root, void* ud) {
    > BaseDocument* doc = GetActiveDocument();
    > if (doc) return doc->GetFirstObject();
    > else return NULL;
    > }
    >
    > virtual void* GetNext(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetNext();
    > else return NULL;
    > }
    >
    > virtual void* GetPred(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetPred();
    > else return NULL;
    > }
    >
    > virtual void* GetDown(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetDown();
    > else return NULL;
    > }
    >
    > virtual void* GetUp(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetUp();
    > else return NULL;
    > }
    >
    > virtual Bool IsSelected(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetBit(BIT_ACTIVE);
    > else return FALSE;
    > }
    >
    > virtual void Select(void* root, void* ud, void* obj, LONG mode) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > BaseDocument* doc = GetActiveDocument();
    >
    > switch (mode) {
    > case SELECTION_NEW:
    > if (doc) doc->SetActiveObject(NULL);
    > case SELECTION_ADD:
    > if (op) op->SetBit(BIT_ACTIVE);
    > break;
    > case SELECTION_SUB:
    > if (op) op->DelBit(BIT_ACTIVE);
    > break;
    > }
    > }
    >
    > virtual Bool IsOpened(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetBit(BIT_OFOLD);
    > else return FALSE;
    > }
    >
    > virtual void Open(void* root, void* ud, void* obj, Bool mode) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) {
    > if (mode) op->SetBit(BIT_OFOLD);
    > else op->DelBit(BIT_OFOLD);
    > }
    > }
    >
    > virtual String GetName(void* root, void* ud, void* obj) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    > if (op) return op->GetName();
    > else return "???";
    > }
    >
    > virtual Bool IsChecked(void* root, void* ud, void* obj, LONG column) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    >
    > LONG state = 0;
    > switch (column) {
    > case COLUMN_HIDDEN:
    > if (op) {
    > state |= LV_CHECKBOX_ENABLED;
    >
    > // If the object is hidden in the Object Manager, the checkbox should
    > // be checked.
    > if (op->GetNBit(NBIT_OHIDE)) {
    > state |= LV_CHECKBOX_CHECKED;
    > }
    > }
    > break;
    > }
    >
    > return state;
    > }
    >
    > virtual void SetCheck(void* root, void* ud, void* obj, LONG column, Bool checked, const BaseContainer& msg) {
    > BaseObject* op = static_cast<BaseObject*>(obj);
    >
    > switch (column) {
    > case COLUMN_HIDDEN:
    > if (op) {
    > // Hide the object if the checkbox was checked, unhide if it was unchecked.
    > NBITCONTROL control;
    > if (checked) control = NBITCONTROL_SET;
    > else control = NBITCONTROL_CLEAR;
    > op->ChangeNBit(NBIT_OHIDE, control);
    > EventAdd();
    > }
    > }
    > }
    >
    > virtual VLONG GetId(void* root, void* ud, void* obj) {
    > return reinterpret_cast<VLONG>(obj);
    > }
    >
    > virtual LONG GetDragType(void* root, void* ud, void* obj) {
    > return 0;
    > }
    >
    > };
    >
    > class MyDialog : public GeDialog {
    >
    > TreeViewCustomGui* m_tree;
    > MyTreeViewFunctions m_functions;
    >
    > public:
    >
    > virtual Bool CreateLayout() {
    > BaseContainer data;
    > m_tree = static_cast<TreeViewCustomGui*>(AddCustomGui(
    > 1000, CUSTOMGUI_TREEVIEW, "", BFH_SCALEFIT | BFV_SCALEFIT, 300, 120, data)
    > );
    > return m_tree != NULL;
    > }
    >
    > virtual Bool InitValues() {
    > if (m_tree) {
    > BaseContainer layout;
    > layout.SetLong(COLUMN_NAME, LV_TREE);
    > layout.SetLong(COLUMN_HIDDEN, LV_CHECKBOX);
    > m_tree->SetLayout(2, layout);
    > m_tree->SetHeaderText(COLUMN_NAME, "Name");
    > m_tree->SetHeaderText(COLUMN_HIDDEN, "Hidden");
    > m_tree->SetRoot(NULL, &m_functions, NULL);
    > m_tree->Refresh();
    > }
    >
    > return TRUE;
    > }
    >
    > virtual Bool CoreMessage(LONG type, const BaseContainer& msg) {
    > if (type == EVMSG_CHANGE) {
    > if (m_tree) m_tree->Refresh();
    > }
    > return GeDialog::CoreMessage(type, msg);
    > }
    >
    > };
    >
    > #define PLUGIN_ID 1000010 /* TEST ID */
    >
    > class Command : public CommandData {
    >
    > MyDialog m_dialog;
    >
    > public:
    >
    > virtual Bool Execute(BaseDocument* doc) {
    > return m_dialog.Open(DLG_TYPE_ASYNC, PLUGIN_ID);
    > }
    >
    > };
    >
    > Bool PluginStart() {
    > return RegisterCommandPlugin(
    > PLUGIN_ID,
    > "TreeViewTest",
    > 0,
    > NULL,
    > "",
    > gNew Command);
    > }
    >
    > Bool PluginMessage(LONG type, void* pData) {
    > switch (type) {
    > case C4DPL_INIT_SYS:
    > return resource.Init();
    > };
    > return TRUE;
    > }
    >
    > void PluginEnd() {
    > }

    -Niklas



  • On 13/09/2013 at 17:38, xxxxxxxx wrote:

    Ok. Thanks.
    I see what you're doing by using the object now.
    I don't really want to do that. But at least I see how it works now.
    I will play around with it an see if I can store the bits someplace other than the objects.

    Note: You've got IsChecked() set as a Bool type. But it needs to be a LONG type to compile (for me anyway)
    virtual LONG IsChecked(void* root, void* ud, void* obj, LONG column)

    Thanks a lot for your help.
    This has been driving me insane for the past three days.
    I was close to going postal. LOL

    -ScottA


Log in to reply