• Disable default Right-Click Menu

    python r19 r20 r21
    9
    0 Votes
    9 Posts
    2k Views
    U
    @m_adam Thanks! Thats a perfect explanation that solves many of my issues - even in some of my other Plugins. I am sorry, I did in fact refer to c4d freezing when I said crashing. Sorry for the confusion.
  • Catch other object's delete/undo

    c++ r19 r20 r21
    5
    0 Votes
    5 Posts
    910 Views
    rsodreR
    @m_adam , AddEventNotification was exactly what I needed. I created a notification to NOTIFY_EVENT_UNDO, and then add my object to it. Now if I delete anything from my object after a polygon is deleted, it will be restored with Undo. Thanks! case MSG_NOTIFY_EVENT: { CHECK_BREAK( data != nullptr ); const auto eventData = static_cast<NotifyEventData*>( data ); if( eventData->eventid == NOTIFY_EVENT_UNDO ) { NotifyEventMsg* notifyMessage = static_cast<NotifyEventMsg*>( eventData->event_data ); CHECK_BREAK( notifyMessage != nullptr ); if( notifyMessage->msg_id == Int32( UNDOTYPE_DELETE ) ) { eventData->doc->AddUndo( UNDOTYPE_CHANGE, Get() ); } } break; }
  • Adding polygon UVs & Normals.

    python r20
    13
    0 Votes
    13 Posts
    3k Views
    M
    Just to add additional information, In R21 GetAllHighlevelData() and SetAllHighlevelData() are working as expected for NormalTag, so data are not any more "shifted" and correctly interpreted internally. See What's New: Fixes: Fixed an issue where VariableTag.GetAllHighlevelData() and VariableTag.SetAllHighlevelData() used Uint16 instead of int16 for Tnormal. Fixed an issue where VariableTag.GetAllHighlevelData() returned data were shifted and not complete for Tnormal. Cheers, Maxime.
  • On the problems of C + + novice

    6
    0 Votes
    6 Posts
    924 Views
    S
    Hello, there aren't any official video tutorials regarding plugin development. If you are new to C++, I strongly suggest to learn the language and how to use Visual Studio before diving into Cinema 4D plugin programming. When you are familiar with C++ and Visual Studio, the first thing you should do is to compile the provided example projects. Especially the "microsdk" show the basics of a simple project. How to build the example projects is described here: Getting Started: Introduction. What specific issues do you encounter while compiling the examples? best wishes, Sebastian
  • 0 Votes
    6 Posts
    709 Views
    S
    Hello, just for your information: GetDEnabling() gives you the description of the parameter with the itemdesc parameter. From that BaseContainer, you should be able to read the ID of the parameter's parent group (see Description Settings Manual - Groups). best wishes, Sebastian
  • 0 Votes
    6 Posts
    928 Views
    B
    @Cairyn and @r_gigante Apologies for the late response. Yes, the ExecutePasses works as expected. Thanks for the solution. I can't use the SetMg()/GetMg() because the script is part of a larger base code which is not limited only to the PSR tag. Thanks again. Have a great day ahead!
  • Custom renderer, viewport render crashes

    c++ r21
    3
    0 Votes
    3 Posts
    595 Views
    F
    "I warmly recommend cloning the whole document on a temp doc" Thanks, I'll try working on a clone. I had the impression that the document passed to the videopost was already a clone of the original document, but perhaps this is not the case for viewport rendering? "With regard to your issue, I'm a bit confused by the need you have to call BaseDocument::AnimateObject() or BaseDocument::ExecutePasses() when the VIDEOPOSTCALL in VideoPostData::Execute() is VIDEOPOSTCALL::INNER. Actually when you're in this step, you can already access and query the VolumeData to retrieve all the relevant information about the scene elements (like RayObject, RayLight and so on)." -We have some good reasons for working with the basedocument directly, rather than the data provided from the VolumeData. "Consider that this approach is sub-optimal since you're going to double the resources used to store the current document." -OK. This should be fine for our purposes. Our plugin actually provides custom rendering commands, that are the preferred way of rendering with the plugin. The videopost functionality is added as more of a convenience in our case, to make the plugin work nicely with things like viewport rendering, material preview etc. /Filip
  • Visual Studio - Debugging Plugin Error

    9
    1
    0 Votes
    9 Posts
    1k Views
    S
    Hello, you find information how to run a plugin from within Visual Studio in the documentation: Development for Microsoft Windows - Running Plugins. best wishes, Sebastian
  • Visual Studio - How to build the final plugin version.

    5
    0 Votes
    5 Posts
    761 Views
    mfersaouiM
    Thank you so much, all The Cinema 4D on the other computer was not updated to R19.068, I updated the c4d app and I made new test, it seem to work normally. To be sure, I will retest it again on a third computer. I have an error when I try to debugging plugins, I will create new topic concerning this error. Thanks again, and have a great day.
  • Creating a 'Reload Image' button

    6
    1
    0 Votes
    6 Posts
    1k Views
    S
    Hello, if in doubt, one can always call c4d.StopAllThreads() before editing the active document from some user interaction in the main thread. This call will terminate other threads that may also currently use the active document (e.g. viewport rendering). best wishes, Sebastian
  • Compling against Alembic

    4
    0 Votes
    4 Posts
    718 Views
    r_giganteR
    Hi eldiren, thanks for following up. With regard to the different amount of errors between R19 and R20 without better clarification on what were the errors on R19 I would not argue about things being "better". In R20 plugin project files are created by using the Project Tool whilst in R19 projects had to be setup manually. This could maybe helped to a certain extent. Because of the use of the Project Tool and the need to include both external headers and libraries I recommend to have a look at: including headers adding external libs Finally I recommend to switch to Alembic 1.7.7 which was the revision used in Cinema 4D R20 in order to limit potential conflicts due to the different revision level or to use your source in a separate namespace to make sure “your” Alembic binaries are used. Best, R
  • Prevent c4d from opening path passed in sys.argv

    r20 python windows
    5
    0 Votes
    5 Posts
    925 Views
    B
    Thanks! I'll try that.
  • Find nearest position on a geometry

    r21 python
    6
    0 Votes
    6 Posts
    643 Views
    r_giganteR
    Hi Frank, thanks for reaching out us. With regard to your request, aside from confirming that nothing comes with Python API for obvious performance reasons, in C++ the nearest thing you can find is KDtree - as already pointed out by @zipit - or other similar accelerating structures. On top of these notes, I would consider this topic more a general programming / algorithm-related topic which could find, based on the contour conditions, different "better" solutions. Best, R
  • Show/Hide Tabs in GUI

    python
    3
    0 Votes
    3 Posts
    698 Views
    N
    Hello Sebastian, sorry, for not using the QA System, will do in the future! This was exactly what I needed. I have never changed my GUI outside the res file and it felt kinda confusing to me what exactly I have to do after reading the doc. Thanks for your awnser! Best Regards, Florian
  • How to Open a Dialog Plugin from a Tag Plugin?

    sdk python
    6
    0 Votes
    6 Posts
    1k Views
    S
    Hello, the important question is, if you want to open a modal or asynchronous dialog. An asynchronous dialog must be hosted by a CommandData plugin (GeDialog Manual) (e.g. py-memory_viewer). As mentioned above, a double-click on a tag can be detected by looking for the MSG_EDIT message (NodeData::Message() Manual). So, when you detect MSG_EDIT, you simply have to execute the command that hosts you dialog. You can simply do that using CallCommand() (Command Utility Manual). Within your dialog, you can find your tag by getting the active tag. best wishes, Sebastian
  • What is the difference between CheckType() and IsInstanceOf()?

    r21 python
    3
    1 Votes
    3 Posts
    335 Views
    CairynC
    Yes, C4DAtom.CheckType() in Python. Thanks for the confirmation, it bugged me that it was only in the Python doc but not in the C++ doc.
  • Selecting Objects in Order

    python
    2
    1 Votes
    2 Posts
    2k Views
    M
    Hi @dskeithbuck thanks for reaching us. In fact, it's written in the C++ BaseDocument - Selection Manual but I agree maybe a note in the function will be welcome. IN any case, I will add a note for the python doc. Thanks again. Cheers, Maxime.
  • menu.InsData(, 'CMD') does not work on Tags?

    r21 python
    4
    0 Votes
    4 Posts
    538 Views
    ManuelM
    hello, without more information from your part, i'll close this thread Cheers, Manuel
  • CustomGUI gadget triggered by other gadget

    c++ r19 r20 r21
    10
    1
    0 Votes
    10 Posts
    1k Views
    C4DSC
    @m_magalhaes Thanks for your message-example. I hadn't quite understood it the first time, and needed a re-trigger. Inspired by your reply I came up with this solution instead. While it still is based on messaging, I preferred to use the SpecialEventAdd. This way, I could pass the type value via a "custom message". Obviously the customgui needs to be set up to react accordingly to this SpecialEventAdd, but this is what I sort of expected to already have been built in into the customgui, in order to be able to react to a trigger to update its additional attributes. Now we only can react to the attribute at constructor time. Maybe a request for a future update/addition? Note for others reading this topic: The code below is just a quick solution, having collected code from different sources and examples. It might obviously be optimized and commented in a better way. Also, make sure to register appropriate pluginIDs, as all ones used here are for demonstration purposes only. For the example below I followed a different route for testing purposes. Obviously it would lead me too far to provide a fully working plugin with NodeData etc ... As such, I wen for a simple CommandData with a GeDialog containing a set of radio buttons and a customgui gadget. The radio buttons allow to switch between black/white and r/g/b and c/m/y/k. With each selection the customgui displays a different set of options, showing a colored rectangle per option. The code is R20, but easily adjustable for R21. Main.cpp // ======================== // Cinema 4D C++ plugin // // PluginName: Test // Dummy "empty" plugin // ======================== // Main.cpp #include "c4d.h" // === Registered pluginIDs === #define MYCOMMAND_PLUGIN_ID 1000000 // DUMMY VALUE for demonstration purposes only !!! #define CUSTOMGUI_GADGET_ID 1000002 // DUMMY VALUE for demonstration purposes only !!! // a specific message ID to trigger the gadget to set its type #define CUSTOMGUI_GADGET_SETTYPEMSG_ID 100 // the gadget IDs #define RADIO_BUTTONS 10000 #define CUSTOMGUI_GADGET 10010 extern Bool RegisterGadget(); // ==================================== // GeDialog // ==================================== class MyDialog : public GeDialog { public: MyDialog(void) {} virtual ~MyDialog(void) {} virtual Bool CreateLayout(void); virtual Bool InitValues(void); virtual void DestroyWindow(void); virtual Bool Command(Int32 id, const BaseContainer& msg); virtual Int32 Message(const BaseContainer& msg, BaseContainer& result); }; Bool MyDialog::CreateLayout(void) { Bool res = GeDialog::CreateLayout(); // when using a GeLoadString(<string-id>) // strings need to be defined in the main string resources, not in the dialogs subfolder SetTitle("Test Dialog"_s); GroupBegin(0, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, ""_s, 0); { GroupBorderSpace(4, 4, 4, 4); GroupBegin(0, BFH_LEFT, 2, 0, maxon::String(), 0); { // 3 radio buttons vertically AddRadioGroup(RADIO_BUTTONS, BFV_SCALEFIT, 0, 3); AddChild(RADIO_BUTTONS, 2, "BW"_s); AddChild(RADIO_BUTTONS, 3, "RGB"_s); AddChild(RADIO_BUTTONS, 4, "CMYK"_s); // the custom gadget BaseContainer bc; AddCustomGui(CUSTOMGUI_GADGET, CUSTOMGUI_GADGET_ID, String(), 0, 0, 0, bc); } GroupEnd(); } GroupEnd(); return res; } Bool MyDialog::InitValues(void) { // first call the parent instance if (!GeDialog::InitValues()) return false; // do our thing ... Int32 typevalue = 2; SetInt32(RADIO_BUTTONS, typevalue); // set default to black and white // and inform the gadget about it SpecialEventAdd(CUSTOMGUI_GADGET_ID, CUSTOMGUI_GADGET_SETTYPEMSG_ID, typevalue); return true; } void MyDialog::DestroyWindow(void) {} Bool MyDialog::Command(Int32 id, const BaseContainer& msg) { if (id == RADIO_BUTTONS) { Int32 typevalue; GetInt32(RADIO_BUTTONS, typevalue); // send this to the customgui gadget to update its representation, // using an EventAdd will trigger a "regular" EVMSG_CHANGE // but we prefer to provide a SpecialEventAdd, as such we: // 1. avoid that the gadget is triggered by every EVMSG_CHANGE // 2. allow to specify a specific value to change the type to //EventAdd(); SpecialEventAdd(CUSTOMGUI_GADGET_ID, CUSTOMGUI_GADGET_SETTYPEMSG_ID, typevalue); } return true; } Int32 MyDialog::Message(const BaseContainer& msg, BaseContainer& result) { return GeDialog::Message(msg, result); } // ==================================== // CommandData // ==================================== class MyCommand : public CommandData { INSTANCEOF(MyCommand, CommandData) public: MyDialog dlg; public: virtual Bool Execute(BaseDocument* doc); }; Bool MyCommand::Execute(BaseDocument* doc) { if (dlg.IsOpen() == false) dlg.Open(DLG_TYPE::ASYNC, MYCOMMAND_PLUGIN_ID, -1, -1, 300, 200, 0); return true; } Bool RegisterMyCommand(void) { return RegisterCommandPlugin(MYCOMMAND_PLUGIN_ID, "Test"_s, 0, AutoBitmap("icon.png"_s), "Test"_s, NewObjClear(MyCommand)); } // ==================================== // Plugin Main // ==================================== Bool PluginStart(void) { ApplicationOutput("Test"_s); RegisterMyCommand(); RegisterGadget(); return true; } void PluginEnd(void) { } Bool PluginMessage(Int32 id, void * data) { switch (id) { case C4DPL_INIT_SYS: if (!g_resource.Init()) return false; return true; case C4DMSG_PRIORITY: return true; case C4DPL_BUILDMENU: break; case C4DPL_ENDACTIVITY: return true; } return false; } CustomGUI_Gadget.cpp // CustomGUI_Gadget.cpp // The custom gadget is a sort of horizontal graphical radio button group // the number of buttons is dependent the gadget type // type = 0 -> not initialized, no buttons // type = 2 has 2 buttons (black and white) // type = 3 has 3 buttons (red, green, blue) // type = 4 has 4 buttons (cyan, magenta, yellow, black) #include "c4d.h" #include "lib_clipmap.h" const Int32 kItemSize = 30; // the size of each "button" // === Registered pluginIDs === #define CUSTOMGUI_GADGET_ID 1000002 // DUMMY VALUE for demonstration purposes only !!! #define CUSTOMGUI_GADGET_ATTRIBUTE_ID 1000003 // DUMMY VALUE for demonstration purposes only !!! #define USERAREA_ID 10001 // The ID of the UserArea GUI element. // a specific message ID to trigger the gadget to set its type #define CUSTOMGUI_GADGET_SETTYPEMSG_ID 100 //--------------------------- // The user area used to display the custom datatype //--------------------------- class GadgetUserArea : public GeUserArea { public: GadgetUserArea(); virtual ~GadgetUserArea(); virtual Bool Init(); virtual Bool InitValues(); virtual Bool GetMinSize(Int32& w, Int32& h); virtual void DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg); virtual Bool InputEvent(const BaseContainer& msg); Int32 mSelection; Int32 mType; Vector bw[2]; Vector rgb[3]; Vector cmyk[4]; // use a GeClipMap for drawing AutoAlloc<GeClipMap> mClipmap; }; GadgetUserArea::GadgetUserArea() { mSelection = 0; mType = 0; } GadgetUserArea::~GadgetUserArea() { } Bool GadgetUserArea::Init() { bw[0] = Vector(0); bw[1] = Vector(255); rgb[0] = Vector(255, 0, 0); rgb[1] = Vector(0, 255, 0); rgb[2] = Vector(0, 0, 255); cmyk[0] = Vector(0, 255, 255); cmyk[1] = Vector(255, 0, 255); cmyk[2] = Vector(255, 255, 0); cmyk[3] = Vector(0); return true; } Bool GadgetUserArea::InitValues() { return true; } Bool GadgetUserArea::GetMinSize(Int32& w, Int32& h) { w = kItemSize * 4; h = kItemSize; return true; } void GadgetUserArea::DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg) { OffScreenOn(); if (!mClipmap) return; const Int32 w = GetWidth(); const Int32 h = GetHeight(); mClipmap->Init(w, h, 32); mClipmap->BeginDraw(); // background Int32 r, g, b; GetColorRGB(COLOR_BG, r, g, b); mClipmap->SetColor(r, g, b, 255); mClipmap->FillRect(x1, y1, x2, y2); Vector color; if (mType != 0) { // draw the possible options as background, // then draw the current selected option on top switch (mType) { case 2: // black and white { for (Int32 col = 0; col < 2; ++col) { color = bw[col]; mClipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255); Int32 x = col * kItemSize; mClipmap->FillRect(x + 2, 2, x + kItemSize - 2, kItemSize - 2); } break; } case 3: // RGB { for (Int32 col = 0; col < 3; ++col) { color = rgb[col]; mClipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255); Int32 x = col * kItemSize; mClipmap->FillRect(x + 2, 2, x + kItemSize - 2, kItemSize - 2); } break; } case 4: // CMYK { for (Int32 col = 0; col < 4; ++col) { color = cmyk[col]; mClipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255); Int32 x = col * kItemSize; mClipmap->FillRect(x + 2, 2, x + kItemSize - 2, kItemSize - 2); } break; } } if (mSelection >= 0) { // orange "selection" color GetColorRGB(COLOR_TEXTFOCUS, r, g, b); mClipmap->SetColor(r, g, b, 255); Int32 x = mSelection * kItemSize; mClipmap->Rect(x, 0, x + kItemSize, kItemSize); mClipmap->Rect(x + 1, 1, x + kItemSize - 1, kItemSize - 1); } } mClipmap->EndDraw(); DrawBitmap(mClipmap->GetBitmap(), 0, 0, w, h, 0, 0, w, h, BMP_ALLOWALPHA); } Bool GadgetUserArea::InputEvent(const BaseContainer& msg) { // check the input device switch (msg.GetInt32(BFM_INPUT_DEVICE)) { // some mouse interaction case BFM_INPUT_MOUSE: { // get the cursor position Int32 mx = msg.GetInt32(BFM_INPUT_X); Int32 my = msg.GetInt32(BFM_INPUT_Y); Global2Local(&mx, &my); // Note that the origin of a GeUserArea is upperleft // (which is equal to the 4th quadrant of cartesian coordinate system) mSelection = mx / kItemSize; // inform the parent that the data has changed BaseContainer m(BFM_ACTION); m.SetInt32(BFM_ACTION_ID, GetId()); m.SetData(BFM_ACTION_VALUE, mSelection); SendParentMessage(m); //Redraw(); return true; } } return false; } //---------------------------------------------------------------------------------------- // A custom GUI to display the ReferencePoint //---------------------------------------------------------------------------------------- class iGadget : public iCustomGui { INSTANCEOF(iGadget, iCustomGui) private: // The current tristate. Bool mTristate; // instance of the userarea to display the ReferencePointer GadgetUserArea mUA; public: iGadget(const BaseContainer &settings, CUSTOMGUIPLUGIN *plugin); virtual Bool CreateLayout(); virtual Bool InitValues(); virtual Bool Command(Int32 id, const BaseContainer &msg); virtual Int32 Message(const BaseContainer &msg, BaseContainer &result); virtual Bool SetData(const TriState<GeData> &tristate); virtual TriState<GeData> GetData(); virtual void CustomGuiRedraw(); }; iGadget::iGadget(const BaseContainer &settings, CUSTOMGUIPLUGIN *plugin) : iCustomGui(settings, plugin) { mUA.mType = settings.GetInt32(CUSTOMGUI_GADGET_ATTRIBUTE_ID); //mUA.mType = mType; // pass along the type to the userarea // Defining default values mTristate = false; }; Bool iGadget::CreateLayout() { GroupBegin(1000, BFH_SCALEFIT | BFV_FIT, 1, 1, String(), 0); { GroupSpace(0, 0); // Attach the User Area to the custom GUI AddUserArea(USERAREA_ID, BFH_SCALEFIT, 0, 0); AttachUserArea(mUA, USERAREA_ID); } GroupEnd(); return SUPER::CreateLayout(); }; Bool iGadget::InitValues() { // The data and it's tristate are handled automatically. this->SetInt32(USERAREA_ID, mUA.mSelection, mTristate); return true; }; Bool iGadget::Command(Int32 id, const BaseContainer &msg) { switch (id) { case USERAREA_ID: { // The Gadget was changed. // Update GUI this->InitValues(); // Send message to parent object to update the parameter value. BaseContainer m(BFM_ACTION); m.SetInt32(BFM_ACTION_ID, GetId()); m.SetData(BFM_ACTION_VALUE, msg.GetInt32(BFM_ACTION_VALUE)); SendParentMessage(m); return true; break; } } return SUPER::Command(id, msg); } Int32 iGadget::Message(const BaseContainer &msg, BaseContainer &result) { switch (msg.GetId()) { case BFM_CORE_MESSAGE: { if (!CheckCoreMessage(msg)) { break; } else { if (msg.GetInt32(BFM_CORE_ID) == EVMSG_CHANGE) { // get the parameter and update the userarea if needed //ApplicationOutput("iGadget::Message() detected EVMSG_CHANGE"); } if (msg.GetInt32(BFM_CORE_ID) == CUSTOMGUI_GADGET_ID) { UInt par1 = (UInt)msg.GetVoid(BFM_CORE_PAR1); UInt par2 = (UInt)msg.GetVoid(BFM_CORE_PAR2); //ApplicationOutput("iGadget::Message() detected a SpecialEventAdd @ @", par1, par2); if (par1 == CUSTOMGUI_GADGET_SETTYPEMSG_ID) { // The SpecialEventAdd which is responsable for this message // uses the CUSTOMGUI_GADGET_ID as message ID, // a specific value CUSTOMGUI_GADGET_SETTYPEMSG_ID as first parameter to indicate we want to set the gadget's type, // and finally the type value as the second parameter // -> SpecialEventAdd(CUSTOMGUI_GADGET_ID, CUSTOMGUI_GADGET_SETTYPEMSG_ID, typevalue); // accept the type change for the gadget and trigger an update // (see iGadget::Command, where user interaction in the userarea triggers an update) mUA.mType = (Int32)par2; // reset the selection to avoid "out of range" depending the type mUA.mSelection = 0; CustomGuiRedraw(); } } } break; } } return SUPER::Message(msg, result); } Bool iGadget::SetData(const TriState<GeData> &tristate) { // The data is changed from the outside. mUA.mSelection = tristate.GetValue().GetInt32(); mTristate = tristate.GetTri(); this->InitValues(); // need to update the userarea this->mUA.Redraw(); return true; }; TriState<GeData> iGadget::GetData() { // The data is requested from the outside. TriState<GeData> tri; tri.Add(mUA.mSelection); return tri; }; void iGadget::CustomGuiRedraw() { this->mUA.Redraw(); } //---------------------------------------------------------------------------------------- // This CustomGuiData class registers a new custom GUI for the ReferencePoint datatype. //---------------------------------------------------------------------------------------- class Gadget : public CustomGuiData { public: virtual Int32 GetId(); virtual CDialog* Alloc(const BaseContainer& settings); virtual void Free(CDialog* dlg, void* userdata); virtual const Char* GetResourceSym(); virtual CustomProperty* GetProperties(); virtual Int32 GetResourceDataType(Int32*& table); }; static Int32 g_stringtable[] = { DTYPE_LONG }; //< This array defines the applicable datatypes. static CustomProperty g_GadgetType[] = { { CUSTOMTYPE::LONG, CUSTOMGUI_GADGET_ATTRIBUTE_ID, "TYPE" }, { CUSTOMTYPE::END, 0, "" } }; Int32 Gadget::GetId() { return CUSTOMGUI_GADGET_ID; }; CDialog* Gadget::Alloc(const BaseContainer& settings) { // Creates and returns a new sub-dialog. iGadget* dlg = NewObjClear(iGadget, settings, GetPlugin()); if (!dlg) return nullptr; CDialog *cdlg = dlg->Get(); if (!cdlg) return nullptr; return cdlg; }; void Gadget::Free(CDialog* dlg, void* userdata) { // Destroys the given subdialog. if (!dlg || !userdata) return; iGadget* sub = static_cast<iGadget*>(userdata); DeleteObj(sub); }; const Char* Gadget::GetResourceSym() { // Returns the resource symbol. This symbol can be used in resource files in combination with "CUSTOMGUI". return "TYPE"; }; CustomProperty* Gadget::GetProperties() { // This method can return a pointer to a data structure holding various additional properties. return g_GadgetType; }; Int32 Gadget::GetResourceDataType(Int32*& table) { // Returns the applicable datatypes defined in the stringtable array. table = g_stringtable; return sizeof(g_stringtable) / sizeof(Int32); }; Bool RegisterGadget() { // only register the custom GUI when not already registered by another plugin if (IsLibraryInstalled(CUSTOMGUI_GADGET_ID)) return true; static BaseCustomGuiLib myGadgetLib; ClearMem(&myGadgetLib, sizeof(myGadgetLib)); FillBaseCustomGui(myGadgetLib); if (!InstallLibrary(CUSTOMGUI_GADGET_ID, &myGadgetLib, 1000, sizeof(myGadgetLib))) return false; if (!RegisterCustomGuiPlugin(/*GeLoadString(IDS_CUSTOMGUISTRING)*/"Gadget"_s, 0, NewObjClear(Gadget))) return false; return true; } With this I guess the topic can be closed. But feel free to provide further comments if I overlooked something.
  • What's wrong with GetSelection()?

    r21 python
    7
    0 Votes
    7 Posts
    827 Views
    M
    Hi @Cairyn thanks for spotting it out, it's an issue in the python documentation, if you take a look at the C++ documentation it's said that it supports only Objects and Tags (see BaseDocument Selections Manual). (I will fix the Python doc) On other hand, I confirm to deselect all objects/tags the best way is to call SetSelection with None/nullptr. And finally, as zipit said, in the end, it's only a wrapper around BIT_ACTIVE with the benefic of GetSelection, that the selection is cached so you avoid the iteration on the whole scene. Cheers, Maxime.