ToolData<--> Subdialog Communication?



  • On 27/05/2014 at 10:08, xxxxxxxx wrote:

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

    ---------
    Can anyone tell me how to make the ToolData class communicate with it's subdialog?
    I want to make the MouseInput() code do different things, depending on the options set in the tool's subdialog. But I can't get it to communicate with the dialog's gizmos.

    #include "c4d.h"  
    #include "c4d_symbols.h"  
    #include "mytool.h"  
    #include "ge_dynamicarray.h"  
      
    #define PLUGIN_ID    12345678  
      
    enum  
    {  
      MY_BOX = 1111,  
    };  
      
    //////////////// The sub dialog class  
    class ToolDialog: public SubDialog  
    {  
      public:  
          virtual          Bool InitDialog(void);  
          virtual          Bool CreateLayout(void);  
          virtual          Bool InitValues(void);  
          virtual          Bool Command(LONG id,const BaseContainer &msg);  
    };  
      
    Bool ToolDialog::InitDialog(void)  
    {  
      BaseContainer *toolData = GetToolData(GetActiveDocument(), PLUGIN_ID);  
      if (!toolData) return FALSE;  
      
      return TRUE;  
    }  
      
    Bool ToolDialog::CreateLayout(void)  
    {  
      GroupBegin(0,BFV_SCALEFIT|BFH_SCALEFIT,0,4,"GUI Options",0);  
      GroupBorder(BORDER_GROUP_IN|BORDER_WITH_TITLE);  
      {  
          C4DGadget *g1 = AddCheckbox(MY_BOX, BFH_LEFT, 0, 0, "Enabled");  
      }  
      GroupEnd();  
      
      
      return TRUE;  
    }  
      
    Bool ToolDialog::InitValues(void)  
    {  
      SetBool(MY_BOX, FALSE);  
      
      return TRUE;  
    }  
      
    Bool ToolDialog::Command(LONG id, const BaseContainer &msg)  
    {  
      BaseDocument *doc = GetActiveDocument();  
      Bool enabled;  
      
      if(id == MY_BOX)  
      {  
          this->GetBool(MY_BOX, enabled);  
          GePrint("Gizmo value sent by the dialog class= " + RealToString(enabled));  
      }  
      
      return TRUE;  
    }  
      
      
      
    //////////////// The Tool class  
    class MyTool : public ToolData  
    {  
      public:  
          ToolDialog *dlg;   
      
          virtual LONG                GetToolPluginId() { return PLUGIN_ID; }  
          virtual SubDialog           *AllocSubDialog(BaseContainer *bc);  
          virtual Bool                InitTool(BaseDocument* doc, BaseContainer& data, BaseThread* bt);  
          virtual const String        GetResourceSymbol() { return String("mytool"); }  //The name of the .res file  
          virtual Bool                GetCursorInfo(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, Real x, Real y, BaseContainer &bc);  
          virtual Bool                MouseInput(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, EditorWindow *win, const BaseContainer &msg);      
    };  
      
    Bool MyTool::InitTool(BaseDocument* doc, BaseContainer& data, BaseThread* bt)  
    {  
      return TRUE;  
    }  
      
    Bool MyTool::GetCursorInfo(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, Real x, Real y, BaseContainer &bc)  
    {  
      return TRUE;  
    }  
      
    Bool MyTool::MouseInput(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, EditorWindow *win, const BaseContainer &msg)  
    {  
      Real mx = msg.GetReal(BFM_INPUT_X);  
      Real my = msg.GetReal(BFM_INPUT_Y);  
      
      LONG button;  
      switch (msg.GetLong(BFM_INPUT_CHANNEL))  
      {  
          case BFM_INPUT_MOUSELEFT : button=KEY_MLEFT; break;  
          case BFM_INPUT_MOUSERIGHT: button=KEY_MRIGHT; break;  
          default: return TRUE;  
      }  
      
      BaseContainer device;  
      Real dx,dy;  
      win->MouseDragStart(button,mx,my,MOUSEDRAGFLAGS_DONTHIDEMOUSE|MOUSEDRAGFLAGS_NOMOVE);  
      while (win->MouseDrag(&dx,&dy,&device)==MOUSEDRAGRESULT_CONTINUE)  
      {  
          GePrint("LMB is down");  
          Bool enabled;  
          dlg->GetBool(MY_BOX, enabled);                               //<--------Not working!?  
          GePrint("Gizmo value from tool= " + LongToString(enabled));  //<--------Not working!?  
       
          DrawViews(DRAWFLAGS_ONLY_ACTIVE_VIEW|DRAWFLAGS_NO_THREAD|DRAWFLAGS_NO_ANIMATION);  
      }  
      
      if(win->MouseDragEnd()==MOUSEDRAGRESULT_FINISHED)  
      {  
          GePrint("LMB Was released");  
          doc->DoUndo(TRUE);  
      }  
      
      return TRUE;  
    }  
      
      
    SubDialog *MyTool::AllocSubDialog(BaseContainer *bc)  
    {  
      return gNew ToolDialog();   
    }  
      
    Bool RegisterMyTool()  
    {  
      return RegisterToolPlugin(PLUGIN_ID, GeLoadString(IDS_MYTOOL), 0, AutoBitmap("icon.png"),"C++ ToolData Example",gNew MyTool);  
    }
    

    -ScottA



  • On 27/05/2014 at 13:32, xxxxxxxx wrote:

    Never mind. I found it.
    I had to change this part:

    SubDialog *MyTool::AllocSubDialog(BaseContainer *bc)  
    {  
      dlg = gNew ToolDialog();  
      return dlg;  
    }
    

    Rather than delete this I'm going to leave it here. Because there isn't a single example of how to make a ToolData plugin with a subdialog in the forums. Or in the SDK.
    The only one in the SDK is the Liquid Paining tool. But it doesn't show you how to communicate with the subdialog.

    -ScottA



  • On 28/05/2014 at 07:37, xxxxxxxx wrote:

    Howdy,

    Try this:

    #include "c4d.h"
    #include "c4d_symbols.h"
    #include "mytool.h"
    #include "ge_dynamicarray.h"
      
    #define PLUGIN_ID    12345678
      
    enum
    {
      MY_BOX = 1111,
    };
      
    //////////////// The sub dialog class
    class ToolDialog: public SubDialog
    {
        public:
            Bool enabled;
      
            virtual          Bool InitDialog(void);
            virtual          Bool CreateLayout(void);
            virtual          Bool InitValues(void);
            virtual          Bool Command(LONG id,const BaseContainer &msg);
    };
      
    Bool ToolDialog::InitDialog(void)
    {
        BaseContainer *toolData = GetToolData(GetActiveDocument(), PLUGIN_ID);
        if (!toolData) return FALSE;
      
        return TRUE;
    }
      
    Bool ToolDialog::CreateLayout(void)
    {
        GroupBegin(0,BFV_SCALEFIT|BFH_SCALEFIT,0,4,"GUI Options",0);
        GroupBorder(BORDER_GROUP_IN|BORDER_WITH_TITLE);
        {
            C4DGadget *g1 = AddCheckbox(MY_BOX, BFH_LEFT, 0, 0, "Enabled");
        }
        GroupEnd();
      
      
        return TRUE;
    }
      
    Bool ToolDialog::InitValues(void)
    {
        SetBool(MY_BOX, FALSE);
      
        return TRUE;
    }
      
    Bool ToolDialog::Command(LONG id, const BaseContainer &msg)
    {
        BaseDocument *doc = GetActiveDocument();
        //Bool enabled;
      
        if(id == MY_BOX)
        {
            this->GetBool(MY_BOX, enabled);
            GePrint("Gizmo value sent by the dialog class= " + RealToString(enabled));
        }
      
        return TRUE;
    }
      
      
      
    //////////////// The Tool class
    class MyTool : public ToolData
    {
        public:
            ToolDialog *dlg; 
      
            virtual LONG                GetToolPluginId() { return PLUGIN_ID; }
            virtual SubDialog           *AllocSubDialog(BaseContainer *bc);
            virtual Bool                InitTool(BaseDocument* doc, BaseContainer& data, BaseThread* bt);
            virtual const String        GetResourceSymbol() { return String("mytool"); }  //The name of the .res file
            virtual Bool                GetCursorInfo(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, Real x, Real y, BaseContainer &bc);
            virtual Bool                MouseInput(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, EditorWindow *win, const BaseContainer &msg);    
    };
      
    Bool MyTool::InitTool(BaseDocument* doc, BaseContainer& data, BaseThread* bt)
    {
        return TRUE;
    }
      
    Bool MyTool::GetCursorInfo(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, Real x, Real y, BaseContainer &bc)
    {
        return TRUE;
    }
      
    Bool MyTool::MouseInput(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, EditorWindow *win, const BaseContainer &msg)
    {
        Real mx = msg.GetReal(BFM_INPUT_X);
        Real my = msg.GetReal(BFM_INPUT_Y);
      
        LONG button;
        switch (msg.GetLong(BFM_INPUT_CHANNEL))
        {
            case BFM_INPUT_MOUSELEFT : button=KEY_MLEFT; break;
            case BFM_INPUT_MOUSERIGHT: button=KEY_MRIGHT; break;
            default: return TRUE;
        }
      
        BaseContainer device;
        Real dx,dy;
        win->MouseDragStart(button,mx,my,MOUSEDRAGFLAGS_DONTHIDEMOUSE|MOUSEDRAGFLAGS_NOMOVE);
        while (win->MouseDrag(&dx,&dy,&device)==MOUSEDRAGRESULT_CONTINUE)
        {
            GePrint("LMB is down");
            //Bool enabled;
            //dlg->GetBool(MY_BOX, enabled);                               //<--------Not working!?
            //GePrint("Gizmo value from tool= " + LongToString(enabled));  //<--------Not working!?
            GePrint("Gizmo value from tool= " + LongToString(dlg->enabled));  //<--------Working now?
     
            DrawViews(DRAWFLAGS_ONLY_ACTIVE_VIEW|DRAWFLAGS_NO_THREAD|DRAWFLAGS_NO_ANIMATION);
        }
      
        if(win->MouseDragEnd()==MOUSEDRAGRESULT_FINISHED)
        {
            GePrint("LMB Was released");
            doc->DoUndo(TRUE);
        }
      
        return TRUE;
    }
      
      
    SubDialog *MyTool::AllocSubDialog(BaseContainer *bc)
    {
        return gNew ToolDialog(); 
    }
      
    Bool RegisterMyTool()
    {
      return RegisterToolPlugin(PLUGIN_ID, GeLoadString(IDS_MYTOOL), 0, AutoBitmap("icon.png"),"C++ ToolData Example",gNew MyTool);
    }
    

    Adios,
    Cactus Dan



  • On 28/05/2014 at 08:27, xxxxxxxx wrote:

    Thanks Dan.
    Please check my thinking on this.

    If I use this:

    SubDialog *MyTool::AllocSubDialog(BaseContainer *bc)  
    {  
      return gNew ToolDialog();   
    }
    

    Then I can only get the gizmos from the subdialog if they are a public class member in the subdialog

    But...

    If I use this code

    SubDialog *MyTool::AllocSubDialog(BaseContainer *bc)  
    {  
      dlg = gNew ToolDialog();  
      return dlg;  
    }
    

    Then I can get the gizmos without needing to make them public class member variables

    The second way seems to be more powerful, and it's less code to write.
    But I'm also wondering if there are any down sides, or dangers to using that method?

    -ScottA



  • On 28/05/2014 at 08:43, xxxxxxxx wrote:

    Are you sure your example works, Cactus?

    Just like in Scotts example, MyTool::dlg is never set and will therefore cause undefined
    behaviour in MouseInput().

    Originally posted by xxxxxxxx

    SubDialog *MyTool::AllocSubDialog(BaseContainer *bc)
    {
        return gNew ToolDialog(); 
    }
    

    Then I can only get the gizmos from the subdialog if they are a public class member in the subdialog

    "can only get the gizmos ... if they are public class members" , what more do you want?
    But let me tell you, you won't be able as MyDlg::dlg is never set and is either some gibberish
    value or nullptr.

    You should do it this way:

    class MyTool : public ToolData
    {
        ToolDialog* dlg;
      
      public:
      
        MyTool()
        : ToolData(), dlg(nullptr)
        { }
      
        virtual void FreeTool(BaseDocument* doc, BaseContainer& data)
        {
            dlg = nullptr;
        }
      
        virtual SubDialog* AllocSubDialog(BaseContainer* bc)
        {
            dlg = NewObj(ToolDialog);
            return dlg;
        }
      
    };
    

    -Niklas



  • On 28/05/2014 at 09:12, xxxxxxxx wrote:

    Howdy,

    Originally posted by xxxxxxxx

    Are you sure your example works, Cactus?

    Hehe, well actually no because I forgot to make the change in the code that I posted that ScottA made. My initial suggestion was to show the simplicity of the member variable access.

    Originally posted by xxxxxxxx

    ...Please check my thinking on this...

    Well, either way works.

    In the SDK example liquidtool.cpp, the communication between the dialog and the tool is done through the general function GetToolData(), which returns the tool plugin's BaseContainer. The example doesn't show any code, though, on how to use it, but you'd get the values of the gizmos in GeDialog::Command() and then store them in the tool's container.

    The other method of allocating the dialog to the "dlg" pointer gives you direct access to the sub dialog's public members through the use of dlg->XXXX.

    The amount of code to write would be less in the second method, but I don't know if I'd say it's more powerful. Using the first method and then storing values in the tools container might be the more powerful method because when the user accesses the tool again, the previous settings can be read from the tool's container with GetToolData() and the dialog gizmos set to those previous values in the GeDialog::InitValues() function. That type of behavior makes the tool more user friendly when the user is constantly switching back and forth between tools. 😉

    Adios,
    Cactus Dan



  • On 28/05/2014 at 09:22, xxxxxxxx wrote:

    Good point Niklas.
    There's probably no way the subdialog can't be found. But it's good practice to use a nullptr check.
    I just tried Dan's code and it doesn't work for me either.

    After thinking about it some more. It's not really less code using the pointer method.
    I'm just creating the code in the tool class rather than the dialog class. *duh*

    The main thing I was curious about is if there are any down sides (or dangers) to using a pointer to the other class. Rather than using the public class members.
    I tend to use this a lot for no other reason than force of habit. And since I have nobody checking my work. I often wonder if I'm doing things wrong.

    -ScottA

    Oops- Cross posted with you there Dan



  • On 28/05/2014 at 09:32, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    previous settings can be read from the tool's container with GetToolData() and the dialog gizmos set to those previous values in the GeDialog::InitValues() function. That type of behavior makes the tool more user friendly when the user is constantly switching back and forth

    See....That's why I ask these annoying questions. 🙂
    I was just wondering to myself how do I to get around that problem of the tool resetting every time it's dropped and then re-selected?

    I still don't understand how the GetToolData() part works.
    Can you possibly give an example?
    I can send you my plugin's source code if like to save you the time of building all the files yourself.
    We really should have a small working example of this for people to learn from.

    Thanks,
    -ScottA



  • On 28/05/2014 at 18:21, xxxxxxxx wrote:

    I figured out how to use GetToolData() to keep the gizmos from re-setting.
    It's working fine for me using a pointer to the subdialog class.

    I posted a working example that can be downloaded from my plugins site if anyone needs it:
    https://sites.google.com/site/scottayersmedia/plugins

    -ScottA


Log in to reply