Description Manual

About

A Description object stores information on how C4DAtom parameters are to be displayed in the GUI (Attribute Manager). It contains information on:

  • the parameter type and valid parameter values.
  • optional information on the (custom) GUI that is used to display the parameter in the Attribute Manager.

The information is stored using description setting IDs, see Description Settings Manual.

Note
A Description is displayed in a GeDialog using the DescriptionCustomGui custom GUI element.
User data parameters are stored in a DynamicDescription object, see DynamicDescription Manual.

Access

To add dynamic parameters to a NodeData based plugin one can implement NodeData::GetDDescription(). This function receives a given Description object that should be filled with the description of the visible parameters.

See NodeData::GetDDescription() Manual.

// This example defines the element's parameter description by loading the registered description of the plugin type.
virtual Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags)
{
if (!node || !description)
return false;
// load the description of this type
if (!description->LoadDescription(node->GetType()))
return false;
return SUPER::GetDDescription(node, description, flags);
}

Correspondingly it is possible to get the parameter description from a C4DAtom:

See C4DAtom Parameter Properties.

// This example prints the names of all parameters in the current parameter description.
if (description == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
// get the Description of the given object
if (!object->GetDescription(description, DESCFLAGS_DESC::NONE))
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
void* handle = description->BrowseInit();
const BaseContainer* bc = nullptr;
DescID id, gid;
// loop through all elements of the description
while (description->GetNext(handle, &bc, id, gid))
{
ApplicationOutput("Parameter Name: " + bc->GetString(DESC_NAME));
}
description->BrowseFree(handle);

Allocation/Deallocation

A Description object can be created with the usual tools:

This is needed to access the parameter description of a given object:

// This example accesses the Description of the given object.
// check if an object is selected
BaseObject* const activeObject = doc->GetActiveObject();
if (activeObject == nullptr)
return maxon::OK;
// check if the Description object could be allocated
if (description == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
// read the Description from the active object
if (activeObject->GetDescription(description, DESCFLAGS_DESC::NONE))
{
ApplicationOutput("Got Description");
}

Load Description

The description of an plugin's static parameters is defined in the plugin's *.res file. This *.res file is typically registered using the "Register" function of the plugin (see General Plugin Information Manual). Such a registered description can then be loaded:

A custom data type can also define its own description. This sub-description (or sub-channels) can be loaded with:

// This example reads and prints the sub-description
// of the "Scale" spline parameter of the given "Sweep" generator.
// load parameter sub-description
if (elements == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
elements->Append(sweepObj);
if (desc == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
// get plugin structure for the given resource plugin
RESOURCEDATATYPEPLUGIN* resDataType = FindResourceDataTypePlugin(completeID[-1].dtype);
if (resDataType == nullptr)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
// load sub-description
const Bool gotSubDescription = desc->GetSubDescriptionWithData(
completeID, elements, resDataType, BaseContainer(), nullptr);
if (!gotSubDescription)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
// print names of sub-description parameters
void* handle = desc->BrowseInit();
const BaseContainer* bc = nullptr;
DescID loopID, gid;
// loop through sub-description parameters
while (desc->GetNext(handle, &bc, loopID, gid))
desc->BrowseFree(handle);

Parameter Descriptions

Edit

In certain situations NodeData::GetDDescription() is not called to get the description of all parameters but of only one specific parameter.

Existing parameter descriptions are stored in BaseContainers that can be accessed with:

Note
The parameter IDs are described in Description Settings Manual.
// This example changes the name of a description element.
const DescID* singleid = description->GetSingleDescID();
// parameter ID
DescID cid = DescLevel(EXAMPLE_GENERATOR_BUTTON, DTYPE_BUTTON, 0);
// check if this parameter ID is requested
if (!singleid || cid.IsPartOf(*singleid, nullptr))
{
BaseContainer* settings = nullptr;
settings = description->GetParameterI(cid, nullptr);
if (settings)
settings->SetString(DESC_SHORT_NAME, "New Button Name"_s);
}

Also a new parameter description can be added:

// This example adds a new parameter description.
const DescID* singleid = description->GetSingleDescID();
// add a parameter description
DescID cid = DescLevel(1003, DTYPE_REAL, 0);
// check if this parameter ID is requested (NOTE: this check is important for speed)
if (!singleid || cid.IsPartOf(*singleid, nullptr))
{
// define the new parameter description
settings.SetString(DESC_NAME, "A new parameter"_s);
// set the new parameter
if (!description->SetParameter(cid, settings, ID_OBJECTPROPERTIES))
return;
}

Iterate

There are two ways to iterate over all elements stored in a Description object. The first way is to loop over all stored parameters in a list:

// This example shows how to load an existing description into a temporary
// description and to copy the data into the actual description.
// load the Description of the material preview element
if (tempDesc && tempDesc->LoadDescription("Mpreview"))
{
void* handle = tempDesc->BrowseInit();
const BaseContainer* settings = nullptr;
DescID id, gid;
// loop through the elements of the description
while (tempDesc->GetNext(handle, &settings, id, gid))
{
description->SetParameter(id, *settings, gid);
}
tempDesc->BrowseFree(handle);
}

The other way to iterate over all elements is to navigate down the tree hierarchy defined by parameter groups:

// This example loops through the top level parameter descriptions.
BaseObject* const object = doc->GetActiveObject();
if (object == nullptr)
return maxon::OK;
if (description == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
// read the Description from the active object
if (!object->GetDescription(description, DESCFLAGS_DESC::NONE))
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
// loop through description
// get the first element
if (ar == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
DescEntry* entry = description->GetFirst(ar);
while (entry)
{
// get element
const BaseContainer* bc = nullptr;
DescID descid;
description->GetDescEntry(entry, &bc, descid);
// print description name
if (bc)
// handle sub-entries (elements of parameter groups etc.)
// HandleSubEntries() is a custom function
DescEntry* const subEntries = description->GetDown(entry);
HandleSubEntries(subEntries);
entry = description->GetNext(entry);
}

Miscellaneous

This convenience function allows to fill the BaseContainer for a pop-up menu based on the content of the Description:

// This example creates a pop up menu based on the given Description.
BaseContainer descriptionPopUp;
if (!description->CreatePopupMenu(descriptionPopUp))
return maxon::UnknownError(MAXON_SOURCE_LOCATION);
ShowPopupMenu(nullptr, MOUSEPOS, MOUSEPOS, descriptionPopUp);

This function allows to complete a given DescID:

// This example completes the given DescID.
const DescID incompleteID = DescID(DescLevel(1110, 0, 0));
DescID completeID;
if (!arr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
// check if the ID exists and complete it
if (description->CheckDescID(incompleteID, arr, &completeID))
{
// check if the completed ID describes a float parameter
if (completeID[-1].dtype == DTYPE_REAL)
ApplicationOutput("This is a float parameter");
}

Further Reading