Getting Started: Application

Overview

  • Generic GUI actions are implemented with the CommandData plugin class.
  • A custom window is based on GeDialog which has to be stored within a CommandData plugin.
  • The window layout can be defined in code or using a resource file.
  • File and file names are handled with maxon::Url and Filename classes.
  • The content of files can be read using file streams.

Adding new Commands

A command is an action that is presented in Cinema 4D's user interface. The command can be invoked by pressing the button in the interface or calling CallCommand() with the command ID.

A custom command is created by implementing a CommandData based class. CommandData::Execute() receives a pointer to the currently active BaseDocument.

See also General Plugin Information Manual and Command Utility Manual.

// This example shows the most simple version of a CommandData plugin.
class ExampleCommand : public CommandData
{
INSTANCEOF(ExampleCommand, CommandData)
public:
Bool Execute(BaseDocument* doc, GeDialog* parentManager)
{
ApplicationOutput("Hello Cinema 4D!");
return true;
};
static ExampleCommand* Alloc() { return NewObjClear(ExampleCommand); }
};
#define INSTANCEOF(X, Y)
Definition: c4d_baselist.h:38
Definition: c4d_basedocument.h:498
Definition: c4d_commanddata.h:65
virtual Bool Execute(BaseDocument *doc, GeDialog *parentManager)
Definition: c4d_gui.h:1113
maxon::Bool Bool
Definition: ge_sys_math.h:55
#define ApplicationOutput(formatString,...)
Definition: debugdiagnostics.h:210
const char * doc
Definition: pyerrors.h:226

A custom command plugin must be registered with RegisterCommandPlugin(). The function must be called in the context of PluginStart().

See also Plugin Functions Manual.

// This example shows how to use a "Register" function to register a plugin.
// registers a CommandData plugin
RegisterCommandPlugin(pluginID, "Example Command"_s, 0, nullptr, ""_s, ExampleCommand::Alloc());
Bool RegisterCommandPlugin(Int32 id, const maxon::String &str, Int32 info, BaseBitmap *icon, const maxon::String &help, CommandData *dat)

Creating new Dialogs

GeDialog is the base class for custom dialog windows. A custom window is created by implementing a GeDialog based class.

See GeDialog Manual.

// This example shows the most simple implementation of a GeDialog based window.
class ExampleDialog : public GeDialog
{
INSTANCEOF(ExampleDialog, GeDialog)
public:
{
SetTitle("Example Dialog"_s);
return true;
}
};
void SetTitle(const maxon::String &title)
virtual Bool CreateLayout()

A dialog window can be displayed modal/synchronously (blocking) or non-modal/asynchronously. Such an asynchronous dialog instance must be stored in memory. It is typically stored as a member of a CommandData plugin. The CommandData plugin can be used to open and close the dialog. CommandData::RestoreLayout() must be implemented to handle the dialog correctly in a layout.

// This example shows a CommandData plugin that hosts and opens a GeDialog based window.
class ExampleCommand : public CommandData
{
INSTANCEOF(ExampleCommand, CommandData)
private:
ExampleDialog _dialog;
public:
Bool Execute(BaseDocument* doc, GeDialog* parentManager)
{
if (_dialog.IsOpen() == false)
{
// open dialog window
_dialog.Open(DLG_TYPE::ASYNC, g_exampleDialogID, -1, -1, 400, 400);
}
else
{
// close dialog window
_dialog.Close();
}
return true;
};
Bool RestoreLayout(void* secret)
{
return _dialog.RestoreLayout(g_exampleDialogID, 0, secret);
};
static ExampleCommand* Alloc() { return NewObjClear(ExampleCommand); }
};
virtual Bool RestoreLayout(void *secret)
@ ASYNC
Non-modal (asynchronous) dialog.

Dialog Layout

The layout is defined with groups that contain various GUI gadgets. It is possible to define the layout by implementing GeDialog::CreateLayout(). It is also possible to define the layout in a layout file.

See Layout and Resource Files Manual.

// This example shows how to add basic dialog gadgets
// to the layout and how to define the default value.
Bool CreateLayout()
{
SetTitle("Example Dialog"_s);
// group with two columns
GroupBegin(100, BFH_SCALEFIT, 2, 0, ""_s, 0, 0, 300);
AddEditNumber(g_numberID, BFH_SCALEFIT, 0, 10);
AddButton(g_buttonID, BFH_SCALEFIT, 0, 10, "Increase"_s);
GroupEnd();
return true;
}
Bool InitValues()
{
// set the value for the edit number gadget
SetInt32(g_numberID, 123);
return true;
}
@ BFH_SCALEFIT
Scale fit. BFH_SCALE|BFH_FIT.
Definition: gui.h:315

Interaction

A user can interact with the gadgets displayed in the dialog. The dialog can react when a value was changed or when a button was pressed. In such a case GeDialog::Command() will be called.

See Interaction and Gadget Values.

// This example shows how to implement GeDialog::Command() to define the user interaction.
Bool Command(Int32 id, const BaseContainer& msg)
{
switch (id)
{
// the number value was changed
case (g_numberID):
{
ApplicationOutput("Value changed");
// get the value
if (GetInt32(g_numberID, value))
{
// print value
const String valueStr = String::IntToString(value);
ApplicationOutput("Value: " + valueStr);
}
break;
}
// the button was pressed
case (g_buttonID):
{
ApplicationOutput("The button was pressed");
// get value
if (GetInt32(g_numberID, value))
{
// increase value
value = value + 1;
// store increased value
SetInt32(g_numberID, value);
}
break;
}
}
return true;
}
PyObject * value
Definition: abstract.h:715
Definition: c4d_basecontainer.h:47
Definition: c4d_string.h:39
static String IntToString(Int32 v)
Definition: c4d_string.h:495
maxon::Int32 Int32
Definition: ge_sys_math.h:60
const char const char * msg
Definition: object.h:438

Handling Files

Files can simply be read using file streams. File names can be handled using the classic Filename class or the maxon::Url class.

See Filename Manual, Url Manual and InputStream Manual.

// This example shows a simple function to load text from a file.
String LoadTextFromFile(const Filename& file)
{
{
err.DiagOutput();
err.DbgStop();
return ""_s;
};
// construct maxon::Url
// check if this is a file
if (url.IoDetect() != maxon::IODETECT::FILE)
iferr_throw(maxon::IoError(MAXON_SOURCE_LOCATION, url, "File not found."_s));
// open input stream and get file size
const maxon::InputStreamRef inputStream = url.OpenInputStream() iferr_return;
const maxon::Int length = inputStream.GetStreamLength() iferr_return;
inputStream.Read(data) iferr_return;
// convert to String
return maxon::String { data };
}
maxon::Url MaxonConvert(const Filename &fn, MAXONCONVERTMODE convertMode)
@ NONE
No check if file exists under case-sensitive drives.
Manages file and path names.
Definition: c4d_file.h:94
Definition: basearray.h:412
ResultMem Resize(Int newCnt, COLLECTION_RESIZE_FLAGS resizeFlags=COLLECTION_RESIZE_FLAGS::DEFAULT)
Definition: basearray.h:1369
Definition: string.h:1235
Definition: url.h:952
Int64 Int
signed 32/64 bit int, size depends on the platform
Definition: apibase.h:188
#define MAXON_SOURCE_LOCATION
Definition: memoryallocationbase.h:67
PyWideStringList Py_ssize_t length
Definition: initconfig.h:448
@ FILE
Url is a file.
#define iferr_scope_handler
Definition: resultbase.h:1402
#define iferr_throw(ERR)
Definition: resultbase.h:1582
#define iferr_return
Definition: resultbase.h:1519
const char const char const char * file
Definition: object.h:439

This function could be used in a dialog like this:

// This example shows the implementation of a simple dialog window
// that offers a button to select and load a text file. The content
// of that text file is displayed in the dialog window text field.
Bool CreateLayout()
{
SetTitle("Example Dialog"_s);
GroupBegin(100, BFH_SCALEFIT, 2, 0, ""_s, 0, 0, 300);
AddMultiLineEditText(g_textID, BFH_SCALEFIT | BFV_TOP, 0, 200, style);
AddButton(g_buttonID, BFH_SCALEFIT | BFV_TOP, 0, 10, "Load Text File"_s);
GroupEnd();
return true;
}
Bool Command(Int32 id, const BaseContainer& msg)
{
switch (id)
{
// the button was pressed
case (g_buttonID):
{
ApplicationOutput("The button was pressed");
// select file
Filename selectedFile;
if (selectedFile.FileSelect(FILESELECTTYPE::ANYTHING, FILESELECT::LOAD, "Load File"_s) == false)
return true;
const String loadedText = LoadTextFromFile(selectedFile);
SetString(g_textID, loadedText);
break;
}
}
return true;
}
Bool FileSelect(FILESELECTTYPE type, FILESELECT flags, const maxon::String &title, const maxon::String &force_suffix=maxon::String())
@ LOAD
Load dialog.
@ ANYTHING
Any file.
@ BFV_TOP
Aligned to the top. 1<<0.
Definition: gui.h:303
@ DR_MULTILINE_WORDWRAP
Word wrap multi-line field.
Definition: gui.h:326