section 5 - creating a menu plugin (part 3)

«  1  2  3

The sequence of events for the dialog box class is as follows. When the plugin is registered with C4D on start up, the dialog class constructor is called. It is not called again. This is important, because (unlike a channel shader, for example) it appears that the constructor is only ever called once; you cannot use it to carry out some kind of initialisation when the user invokes the plugin.

When the plugin is invoked from the Plugins menu, the dialog is opened. Each time, C4D will call CreateLayout() followed by InitValues(). This means that you can set up dialog gadgets in CreateLayout() then set their initial values in InitValues(). This is quite useful because if you need to reset all the values in the dialog you just need to call InitValues() directly.

CreateLayout() function

In CreateLayout() we first need to call the base class’ own CreateLayout() function. Next, we need to load the resource file. To do this, we create a GeResource object, tell it the path to our plugin using GeGetPluginPath(), and then load the resource file corresponding to this dialog box. The code for this is simple:

// create a resource object and initialise it to the plugin's path
GeResource dlg_res;
dlg_res.Init(GeGetPluginPath());
result = LoadDialogResource(IDD_OBJECTEDITOR, &dlg_res, 0);

Our dialog box contains two combo boxes. I much prefer to add the entries to the combo box in code rather than trying to do it using ResEdit. It’s very easy to do. First, we clear any existing entries from the combo box, then add as many entries as we like using the AddChild()function. When calling AddChild(), we need to supply three things: the ID of the combo box itself (which was set when the dialog was built in ResEdit), an ID number for the menu entry, which is really important because that is how we will know which option the user selected in the combo box, and the text string the user will see. In this case we have two combo boxes; although they have the same text entries, the ID numbers are different:

// we have to set up the two combo boxes in the dialog, with IDs to indicate which option was chosen, and some text for the user
// first the editor visibility
FreeChilds(IDC_EDITORVIS); // clear the combo first; if we don't do this C4D may add multiple menu entries
AddChild(IDC_EDITORVIS, 6050, "Always visible");
AddChild(IDC_EDITORVIS, 6051, "Always invisible");
AddChild(IDC_EDITORVIS, 6052, "Depends on parent");

// and now the render visibility
FreeChilds(IDC_RENDERVIS);
AddChild(IDC_RENDERVIS, 6060, "Always visible");
AddChild(IDC_RENDERVIS, 6061, "Always invisible");
AddChild(IDC_RENDERVIS, 6062, "Depends on parent");

Finally, we print an error message to the console if the resource file could not be loaded, and return the result to C4D.

InitValues() function

In InitValues() we need to set the initial values of the gadgets. Again, we first call the equivalent base class function. It makes sense to set the dialog gadgets to the values currently held by the active object. We first get a pointer to the active object – there must be one, since if there wasn’t, the user should not have been able to invoke the plugin. Just in case we arrive here even though there is no active object, we’ll set some default values.

We set the gadget values by using functions belonging to GeDialog. These vary, depending on the type of gadget being set. So there are functions such as SetString() (for labels or text edit boxes), SetReal() (for floating point numbers or percentages), SetBool() (for check boxes), and so on. In this case, as well as SetString(), we will use SetLong() to set the initial value we want displayed in the two combo boxes. The values we pass are those we used in the CreateLayout() function when adding entries to the combo boxes.

If there was a selected object, we do the same thing, but first we get the current values. We can get the object’s name with GetName(), and the values of the editor and render dots with GetEditorMode() and GetRenderMode(). Then we can set these into the dialog gadgets. The relevant code looks like this:

// we want to set the gadgets to the current values of the object, so first we need to get the active object
op = NULL;
doc = GetActiveDocument();
if(doc)
    op = doc->GetActiveObject();

// we can now initialise the dialog gadgets
// set default values if there's no active object (we shouldn't even be here if there's no active object, but just in case...)
if(!op)
{
    SetString(IDC_OBJECTNAME, "");
    SetLong(IDC_EDITORVIS, 6050);
    SetLong(IDC_RENDERVIS, 6060);
}
else
{
    // since we do have an object, get the current object values and set the gadgets to those values
    SetString(IDC_OBJECTNAME, op->GetName());
    // get the current editor visiblity
     mode = op->GetEditorMode();
     // we know that 'mode' will have the value 0, 1, or 2 - so we just add that to the starting index for the combo box entries to reflect the current setting
    // the combo box IDs were set in CreateLayout()
    SetLong(IDC_EDITORVIS, 6050 + mode);
    // get the current render visiblity
    mode = op->GetRenderMode();
    SetLong(IDC_RENDERVIS, 6060 + mode);
}

Command() function

Our dialog is now set up, and all that remains is to respond to the user. This takes place in the Command() virtual function which we need to override. The first parameter passed to Command() is the ID number of the gadget the user has clicked or changed. There is also a base container with additional information, but we don’t need that here.

We only need to respond to the OK and Cancel buttons. In this case, and for demonstration purposes only, we also respond to any other gadget by printing its ID number to the console. If you watch the console while interacting with the dialog (by altering the ‘object name’ edit box, for example) you can see that C4D calls Command() whenever a gadget is changed in some way.

The Cancel button is simple. All we do is close the dialog box without doing anything else, using the Close() function of GeDialog. The user can then interact with C4D again.

If the user hits the OK button, we need to make the required changes to the active object. To do this we get the values of the edit text box and the two combo boxes using the GeDialog functions GetString() and GetLong(). (There are additional functions for getting the values of check boxes, integers, floating point numbers, and so on.) Then we just change the corresponding values of the active object using SetName(), SetEditorMode(), and SetRenderMode(). The code for the OK button would look like this:

case 1: // this is the OK button, so we make the changes to the object
    if(op) // make sure we have a valid pointer to the active object
    {
        // get the active document so we can implement undos
        doc = GetActiveDocument();
        doc->StartUndo(); // start adding undos, so that every change we make will be regarded as one undo if the user hits Ctrl/Cmd-Z
        GetString(IDC_OBJECTNAME, sName); // get the string from the edit field in the dialog box
        // don't set the object name if the edit field was empty, as we don't want an object with no name
        if(sName.Content())
        {
            doc->AddUndo(UNDO_CHANGE, op); // add an undo
            op->SetName(sName);
        }
        // get the selected editor mode from the relevant combo box in the dialog
        GetLong(IDC_EDITORVIS, mode); // mode will have a value of 6050, 6051, or 6052 as these are the ID values we set in CreateLayout()
        doc->AddUndo(UNDO_CHANGE, op);
        op->SetEditorMode(mode - 6050); // SetEditorMode() expects a value from 0 - 2
        // do the same for the render mode
        GetLong(IDC_RENDERVIS, mode);
        doc->AddUndo(UNDO_CHANGE, op);
        op->SetRenderMode(mode - 6060);
        doc->EndUndo(); // we can stop adding undos
        EventAdd(EVENT_FORCEREDRAW); // tell C4D to update the display, otherwise it will look as if nothing has changed
    }
    Close(); // close the dialog box
    break;

The only other things we need to do are to add the actions we take to the document’s undo list. We do this in case the user wants to undo the changes made with the plugin. Finally, we call C4D’s EventAdd() function to add an event to the event queue; if we don’t do this, when the user hits OK, it will seem as if nothing has changed. In fact, everything has been changed, but that won’t be apparent until the user does something to force a screen redraw, such as rotating the view. By calling EventAdd(), we make C4D display the changes right away.

And that’s it for the menu plugin. If you compile and test it, you can change the name and editor/render visibility of the selected object using the dialog box, just as if you did it in the object manager. If you do make changes, hitting Undo once will reverse all of them.

While not very useful in its own right, you can see that interaction with a dialog is very simple – easier in some ways than interacting with a description resource. You just need to display the dialog box, then handle all interaction from within the dialog.

The source code and all resources for this plugin can be downloaded from the link below. The source code is fully commented and in some cases gives more details than in the text in these pages.

Download plugin source Download source code and resource files (.zip file, 12K)

Back to the main tutorials page

«  1  2  3