#include "amanith/gkernel.h" // Includes for standard registration. #include "amanith/gelement.h" #include "amanith/gproperty.h" #include "amanith/gmultiproperty.h" #include "amanith/1d/gcurve1d.h" #include "amanith/1d/gbeziercurve1d.h" #include "amanith/1d/gbsplinecurve1d.h" #include "amanith/1d/gmulticurve1d.h" #include "amanith/1d/gpolylinecurve1d.h" #include "amanith/1d/ghermitecurve1d.h" #include "amanith/2d/gpixelmap.h" #include "amanith/2d/gfont2d.h" #include "amanith/2d/gmesh2d.h" #include "amanith/2d/gpath2d.h" #include "amanith/2d/gcurve2d.h" #include "amanith/2d/gbeziercurve2d.h" #include "amanith/2d/gbsplinecurve2d.h" #include "amanith/2d/gmulticurve2d.h" #include "amanith/2d/gpolylinecurve2d.h" #include "amanith/2d/ghermitecurve2d.h" #include "amanith/2d/gellipsecurve2d.h" #include "amanith/2d/ganimtrsnode2d.h" #include "amanith/gimpexp.h" #include "amanith/support/gutilities.h" namespace Amanith { typedef GUInt32 (*ProxiesCountFunc)(); typedef GElementProxy* (*ProxyInstanceFunc)(const GUInt32 Index); //********************************************************************* //**************************** GKernel *********************** //********************************************************************* /* The kernel is the core class that makes introspection and the plugins systems possible and fully working. Kernel must be used for these kind of tasks: - Create a class instance, specifying a class descriptor or a class name. Example follows: \code GBezierCurve2D *newBezier1 = kernel->CreateNew(G_BEZIERCURVE2D_CLASSID); GBezierCurve2D *newBezier2 = kernel->CreateNew("GBezierCurve2D"); \endcode - Load external plugins, using functions like LoadPlugins() or LoadPlugin(). - Automatic memory retrieving: when a kernel is deleted all created class instances are deleted. Example follows: \code void FunFunction() { GKernel *newKernel = new GKernel(); GBezierCurve2D *tmpBezier1 = kernel->CreateNew(G_BEZIERCURVE2D_CLASSID); GBezierCurve2D *tmpBezier2 = kernel->CreateNew("GBezierCurve2D"); // Do something with curves... ... delete newKernel; } \endcode When the "delete newKernel" instruction is executed, tmpBezier1 and tmpBezier2 will be automatically freed. - Types introspection: every classes instantiated with a kernel can answer to some useful queries. Example follows: \code GBezierCurve2D *tmpBezier1 = kernel->CreateNew(G_BEZIERCURVE2D_CLASSID); if (tmpBezier1->IsOfType(G_CURVE2D_CLASSID)) { // Now cast is type-safe. GCurve2D *curve = tmpBezier1; } // Here is a type-safe cast; after this instruction, 'nullFont' variable will be NULL becacause the introspection // system knows that 'tmpBezier1' variable is not a GFont2D class or a GFont2D derived class GFont2D *nullFont = tmpBezier1->As(G_FONT2D_CLASSID); // Using a kernel, every instance can be used as "introspection provider"; this means that a class instanced through // kernels have the same inspection power of the same kernel. if (tmpBezier1->IsTypeOfType(G_BSPLINECURVE2D_CLASSID, G_ELEMENT_CLASSID)) printf("I can say without doubts that GBSplineCurve2D class derives from GElement class"); \endcode */ /* Register all standard proxies, and scan Amanith's plugin directory to search for external plugins. Each found plugin will be automatically loaded. */ GKernel::GKernel() : GElement() { RegisterProxies(); GString pluginsDir = SysUtils::AmanithPluginsPath(); LoadPlugins(StrUtils::ToAscii(pluginsDir), G_TRUE); } /* Register all standard proxies, and scan Amanith's plugin directory to search for external plugins. Each found plugin will be automatically loaded. */ GKernel::GKernel(const GElement* Owner) : GElement(Owner) { RegisterProxies(); GString pluginsDir = SysUtils::AmanithPluginsPath(); LoadPlugins(StrUtils::ToAscii(pluginsDir), G_TRUE); } GError GKernel::BaseClone(const GElement& Source) { const GKernel& k = (const GKernel&)Source; // Just to avoid warnings. if (k.Owner()) { } return G_NO_ERROR; } // Register standard proxies. void GKernel::RegisterProxies() { RegisterElementProxy(G_ELEMENT_PROXY); RegisterElementProxy(G_ANIMELEMENT_PROXY); RegisterElementProxy(G_PROPERTY_PROXY); RegisterElementProxy(G_MULTIPROPERTY1D_PROXY); RegisterElementProxy(G_IMPEXP_PROXY); RegisterElementProxy(G_KERNEL_PROXY); // Pixel map plugin. RegisterElementProxy(G_PIXELMAP_PROXY); // JPEG import/export plugin. RegisterElementProxy(G_JPEGIMPEXP_PROXY); // PNG import/export plugin. RegisterElementProxy(G_PNGIMPEXP_PROXY); // Fonts import/export plugin. RegisterElementProxy(G_FONTSIMPEXP_PROXY); // 1D stuff! RegisterElementProxy(G_CURVE1D_PROXY); RegisterElementProxy(G_BEZIERCURVE1D_PROXY); RegisterElementProxy(G_BSPLINECURVE1D_PROXY); RegisterElementProxy(G_MULTICURVE1D_PROXY); RegisterElementProxy(G_POLYLINECURVE1D_PROXY); RegisterElementProxy(G_HERMITECURVE1D_PROXY); // 2D stuff! RegisterElementProxy(G_FONT2D_PROXY); RegisterElementProxy(G_PATH2D_PROXY); RegisterElementProxy(G_CURVE2D_PROXY); RegisterElementProxy(G_BEZIERCURVE2D_PROXY); RegisterElementProxy(G_BSPLINECURVE2D_PROXY); RegisterElementProxy(G_MULTICURVE2D_PROXY); RegisterElementProxy(G_POLYLINECURVE2D_PROXY); RegisterElementProxy(G_HERMITECURVE2D_PROXY); RegisterElementProxy(G_ELLIPSECURVE2D_PROXY); // Animation stuff! RegisterElementProxy(G_CONSTANTPROPERTY1D_PROXY); RegisterElementProxy(G_LINEARPROPERTY1D_PROXY); RegisterElementProxy(G_HERMITEPROPERTY1D_PROXY); RegisterElementProxy(G_TWOHERMITEPROPERTY1D_PROXY); RegisterElementProxy(G_THREEHERMITEPROPERTY1D_PROXY); RegisterElementProxy(G_FOURHERMITEPROPERTY1D_PROXY); RegisterElementProxy(G_TWOLINEARPROPERTY1D_PROXY); RegisterElementProxy(G_THREELINEARPROPERTY1D_PROXY); RegisterElementProxy(G_FOURLINEARPROPERTY1D_PROXY); RegisterElementProxy(G_TWOCONSTANTPROPERTY1D_PROXY); RegisterElementProxy(G_THREECONSTANTPROPERTY1D_PROXY); RegisterElementProxy(G_FOURCONSTANTPROPERTY1D_PROXY); RegisterElementProxy(G_ANIMTRSNODE2D_PROXY); } /* Unload from memory every registered plugins, and free all instanced classes. */ GKernel::~GKernel() { GInt32 i, j; GError err; GString s1, s2, msg; GElement *elem; G_DEBUG("GKernel destructor: destroying undelete instances"); // Check if some instances are already alive. Note, however, destroying must proceed in sequential order! while (gElementsInstances.size() > 0) { elem = gElementsInstances.front(); if (elem) delete elem; } // Unload all external plugins j = (GInt32)gRegisteredProxies.size(); G_DEBUG("Gkernel destructor, begin to unload " + StrUtils::ToString(j) + " proxies"); for (i = 0; i < j; i++) { // If proxy has been loaded from an external file (typically a plugin) we have to unload it from memory. if (gRegisteredProxies[i].gExternal) { // Unload plugin err = gRegisteredProxies[i].gLoader.UnloadPlug(); if (err != G_NO_ERROR) { msg = "Kernel destroying error: unable to unload plugin " + GString(gRegisteredProxies[i].gProxy->ClassID().IDName()); G_DEBUG(msg); } } } } // Unload a single plugin from memory. GError GKernel::UnLoadPlugin(const GClassID& PluginClassID) { GProxyState p; GError err; GInt32 i; // Find a proxy that wraps specified plugin classid. err = FindProxy(PluginClassID, i); // If proxy is not found or it's not external, we will exit; else try to unload it. if ((err == G_NO_ERROR) && (gRegisteredProxies[i].gExternal)) { err = gRegisteredProxies[i].gLoader.UnloadPlug(); } return err; } /* `Path' -- A string containing the path to scan. It must be valid (non null and existing). `FoundPlugins' -- A string list containing full filename of found plugins during scanning. `ScanSubFolders' if true specifies that scanning must be done recursively on all included subdirectories. Return `G_NO_ERROR' in case of success, else an error code. */ GError GKernel::ScanDirectory(const GString& Path, GStringList& FoundPlugins, const GBool ScanSubFolders) { GStringList tmpList; GSystemInfo sInfo; GError err; SysUtils::SystemInfo(sInfo); err = FileUtils::ScanPath(tmpList, StrUtils::ToAscii(Path), ScanSubFolders, G_TRUE, // Add full path to file names. StrUtils::ToAscii(sInfo.DynamicLibExtension)); if (err == G_NO_ERROR) FoundPlugins = tmpList; return err; } /* `FullFileName' a string containing a full plugin filename. It must be non NULL. `Filter' -- An optional class descriptor filter. If specified (non NULL), then plugin will be loaded only if it derives from the specified class identifier.. Return `G_NO_ERROR' in case of success, else an error code. */ GError GKernel::LoadPlugin(const GChar8 *FullFileName, const GClassID* Filter) { GUInt32 i, j, loadedOk; GError err, errSym1, errSym2; ProxiesCountFunc sym1 = NULL; ProxyInstanceFunc sym2 = NULL; GElementProxy *proxy; const GElementProxy *constProxy; GString fName = FullFileName; if (fName.length() == 0) return G_INVALID_PARAMETER; // Set library loader. GPlugLoader loader(FullFileName); // We do not want to automatically unload library from memory. loader.SetUnloadOnDestroy(G_FALSE); // Load dynamic library. err = loader.LoadPlug(); if (err == G_NO_ERROR) { // Resolve ProxiesCount() and ProxyInstance() functions. errSym1 = loader.ResolveSymbol("ProxiesCount", (GPlugSymbolAddress *)&sym1); errSym2 = loader.ResolveSymbol("ProxyInstance", (GPlugSymbolAddress *)&sym2); if ((errSym1 == G_NO_ERROR) && (errSym2 == G_NO_ERROR)) { G_ASSERT((sym1 != NULL) && (sym2 != NULL)); // Extract every exported proxies. j = sym1(); // Trace how many proxy we can register successfully. loadedOk = 0; for (i = 0; i < j; i++) { // get i-th proxy. proxy = sym2(i); if (proxy) { constProxy = proxy; if ((Filter) && (IsTypeOfType(constProxy->ClassID(), *Filter))) { err = RegisterElementProxy(*constProxy, G_TRUE, &loader); if (err == G_NO_ERROR) loadedOk++; } else { err = RegisterElementProxy(*constProxy, G_TRUE, &loader); if (err == G_NO_ERROR) loadedOk++; } } } // If we haven't register one proxy at least, we can unload plugin to retrieve memory. if (loadedOk == 0) // Unload dynamic library from memory. loader.UnloadPlug(); } else // Unload dynamic library from memory. loader.UnloadPlug(); } else // Unload dynamic library from memory. loader.UnloadPlug(); return G_NO_ERROR; } /* `PathName' a string containing a path to scan for plugins. It must be non NULL. `ScanSubFolders' if true specifies that scanning must be done recursively on all included subdirectories. `Filter' an optional class descriptor filter. If specified (non NULL), then found plugins will be loaded only if they derive from the specified class identifier. Return `G_NO_ERROR' in case of success, else an error code. */ GError GKernel::LoadPlugins(const GChar8 *PathName, const GBool ScanSubFolders, const GClassID* Filter) { GError err; GStringList list; // Scan directory for plugins: we want a full (path included) filename. err = ScanDirectory(PathName, list, ScanSubFolders); if (err != G_NO_ERROR) return err; GStringList::const_iterator it; for (it = list.begin(); it != list.end(); ++it) LoadPlugin(StrUtils::ToAscii(*it), Filter); return G_NO_ERROR; } /* `Features' -- List of import/export features supported by this plugin. Return in case of success operation `G_NO_ERROR' and features list will be filled, else an error code. */ GError GKernel::ImpExpFeatures(GDynArray& Features) const { GDynArray impexpProxies; GImpExp *plug; GUInt32 i, j; Features.clear(); // Get all importer and exporter plugins. ChildClassProxies(G_IMPEXP_CLASSID, impexpProxies); j = (GUInt32)impexpProxies.size(); for (i = 0; i < j; i++) { plug = (GImpExp *)impexpProxies[i].Proxy()->CreateNew(this); // Abort error: I have a proxy but i cannot create it! if (!plug) return G_UNKNOWN_ERROR; // Append features. const GDynArray& f = plug->Features(); Features.insert(Features.end(), f.begin(), f.end()); // Delete temporary plugin. delete plug; } return G_NO_ERROR; } /* \param Features a list of import/export features supported by this plugin. \param ID a class descriptor filter. Only features that support this class identifier will be returned Return in case of success operation `G_NO_ERROR' and features list will be filled, else an error code. */ GError GKernel::ImpExpFeatures(const GClassID& ID, GDynArray& Features) const { GDynArray impexpProxies; GImpExp *plug; GUInt32 i, j, k, w; Features.clear(); // Get all importers and exporters plugin. ChildClassProxies(G_IMPEXP_CLASSID, impexpProxies); j = (GUInt32)impexpProxies.size(); for (i = 0; i < j; i++) { plug = (GImpExp *)impexpProxies[i].Proxy()->CreateNew(this); // Abort error: I have a proxy but i cannot create it! if (!plug) return G_UNKNOWN_ERROR; // Append features. const GDynArray& f = plug->Features(); w = (GUInt32)f.size(); for (k = 0; k < w; k++) { if (f[k].ClassID() == ID) Features.push_back(f[k]); } // Delete temporary plugin. delete plug; } return G_NO_ERROR; } /* `FileName' -- A string containing the full file name (path + name + extension) of the file we want to read from. It must be non-empty. `Element' -- A reference to the element that will be filled with information read from file. `Options' -- A string containing a semicolon-separated options. Each option must be in the format name=value. (EX: "allowblanks=false;version=3") `FormatName' an optional filter; it's case insensitive. Return if operation will complete successfully ` G_NO_ERROR will be returned, else an error code. */ GError GKernel::Load(const GChar8 *FileName, GElement& Element, const GChar8 *Options, const GChar8 *FormatName) { GDynArray impexpProxies; GImpExp *plug; GUInt32 i, j; GError err; GBool b1, b2; GString fName, fExt; // Extract file name and extension. fName = StrUtils::OSFixPath(FileName, G_FALSE); fExt = StrUtils::ExtractFileExt(fName); // Check for a non-empty filename. if (fName.length() <= 0) return G_INVALID_PARAMETER; // Get all importers and exporters plugins. ChildClassProxies(G_IMPEXP_CLASSID, impexpProxies); // First step: try with format. j = (GUInt32)impexpProxies.size(); for (i = 0; i < j; i++) { // Create a plugin. plug = (GImpExp *)impexpProxies[i].Proxy()->CreateNew(this); // Abort error: I have a proxy but I cannot create it! if (!plug) return G_UNKNOWN_ERROR; // check for file format support b1 = G_FALSE; if (FormatName) b1 = plug->FormatSupported(FormatName, Element.ClassID(), G_TRUE, G_FALSE); // Check for file extension support. b2 = plug->FileExtensionSupported(StrUtils::ToAscii(fExt), Element.ClassID(), G_TRUE, G_FALSE); // If some support is guaranteed try to load element. if (b1 || b2) { err = plug->Read(FileName, Element, Options); delete plug; return err; } delete plug; } return G_UNSUPPORTED_CLASSID; } /* `FileName' -- A string containing the full file name (path + name + extension) of the file we wanna write to. It must be non-empty. `Element' -- A reference to the element that will be "saved" to file. `Options' -- A string containing a semicolon-separated options. Each option must be in the format name=value. `FormatName' an optional filter; it's case insensitive. Return `G_NO_ERROR' if operation succeeds, an error code otherwise. */ GError GKernel::Save(const GChar8 *FileName, const GElement& Element, const GChar8 *Options, const GChar8 *FormatName) { GDynArray impexpProxies; GImpExp *plug; GUInt32 i, j; GError err; GBool b1, b2; GString fName, fExt; // Extract file name and extension. fName = StrUtils::OSFixPath(FileName, G_FALSE); fExt = StrUtils::ExtractFileExt(fName); // Check for a non-empty filename. if (fName.length() <= 0) return G_INVALID_PARAMETER; // Get all importers and exporters plugin. ChildClassProxies(G_IMPEXP_CLASSID, impexpProxies); // First step: try with format. j = (GUInt32)impexpProxies.size(); for (i = 0; i < j; i++) { // Create a plugin. plug = (GImpExp *)impexpProxies[i].Proxy()->CreateNew(this); // Abort error: I have a proxy but I cannot create it! if (!plug) return G_UNKNOWN_ERROR; // Check for file format support. b1 = G_FALSE; if (FormatName) b1 = plug->FormatSupported(FormatName, Element.ClassID(), G_FALSE, G_TRUE); // Check for file extension support. b2 = plug->FileExtensionSupported(StrUtils::ToAscii(fExt), Element.ClassID(), G_FALSE, G_TRUE); // If some support is guaranteed try to load element. if (b1 || b2) { err = plug->Write(FileName, Element, Options); // Delete(plug); delete plug; return err; } //Delete(plug); delete plug; } return G_UNSUPPORTED_CLASSID; } // Return the Index-th proxy. GError GKernel::Proxy(const GInt32 Index, GProxyState& OutProxy) const { if ((Index < 0) || (Index >= ProxiesCount())) return G_OUT_OF_RANGE; OutProxy = gRegisteredProxies[Index]; return G_NO_ERROR; } // Register an element proxy. GError GKernel::RegisterElementProxy(const GElementProxy& ElementProxy, const GBool External, const GPlugLoader *Loader) { GError err; GProxyState p; GInt32 i; // First try if an existing compatible proxy has been already registered. err = FindProxy(ElementProxy.ClassID(), i); if (err == G_PLUGIN_NOTPRESENT) { p.gProxy = &ElementProxy; p.gExternal = External; if (External) { G_ASSERT(Loader != NULL); p.gLoader = *Loader; } // Add a new proxy to the internal list. gRegisteredProxies.push_back(p); return G_NO_ERROR; } else { G_DEBUG(GString(ElementProxy.ClassID().IDName()) + " already registered!"); return G_PLUGIN_ALREADYLOADED; } } // Find a registered proxy that match passed classID; return NULL if not found. GError GKernel::FindProxy(const GClassID& Class_ID, GInt32& ProxyIndex) const { GInt32 i, j; GProxyState p; j = (GInt32)gRegisteredProxies.size(); for (i = 0; i < j; i++) { p = gRegisteredProxies[i]; if (p.Proxy()->ClassID() == Class_ID) { ProxyIndex = i; return G_NO_ERROR; } } return G_PLUGIN_NOTPRESENT; } // Find a registered proxy that match passed className. GError GKernel::FindProxy(const GString& Class_Name, GInt32& ProxyIndex) const { GInt32 i, j; GProxyState p; j = (GInt32)gRegisteredProxies.size(); for (i = 0; i < j; i++) { p = gRegisteredProxies[i]; if (StrUtils::SameText(p.Proxy()->ClassID().IDName(), Class_Name)) { ProxyIndex = i; return G_NO_ERROR; } } return G_PLUGIN_NOTPRESENT; } // Get class id from a class name. GError GKernel::ClassIDFromClassName(const GString& Class_Name, GClassID& OutID) const { GInt32 i; GError err; err = FindProxy(Class_Name, i); if (err == G_NO_ERROR) OutID = gRegisteredProxies[i].Proxy()->ClassID(); return err; } inline bool ElementsPtrCmp(const GElement *Element1, const GElement *Element2) { const void *e1 = (const void *)Element1; const void *e2 = (const void *)Element2; if (e1 < e2) return true; return false; } GBool GKernel::IsSupported(const GClassID& Class_ID) const { GInt32 i; GError err = FindProxy(Class_ID, i); if (err == G_NO_ERROR) return G_TRUE; return G_FALSE; } // Create new element specifying a class descriptor GElement* GKernel::CreateNew(const GClassID& Class_ID) { GError err; GInt32 i; GElement* newElem; err = FindProxy(Class_ID, i); // Try with kernel's owner (kernel clustering chain). if (err != G_NO_ERROR) { newElem = GElement::CreateNew(Class_ID); return newElem; } else { newElem = gRegisteredProxies[i].Proxy()->CreateNew(this); return newElem; } } // Creation of a new element specifying a class name. GElement* GKernel::CreateNew(const GString& ClassName) { if (ClassName.size() == 0) return NULL; GClassID cID; GError err = ClassIDFromClassName(ClassName, cID); if (err == G_NO_ERROR) return CreateNew(cID); else return NULL; } GError GKernel::AddElementReference(const GElement *Element) { if (!Element) return G_INVALID_PARAMETER; GList::iterator it; /* it = std::lower_bound(gElementsInstances.begin(), gElementsInstances.end(), DeleteElement, ElementsPtrCmp); if (it == gElementsInstances.end() || (*it) != DeleteElement) gElementsInstances.insert(it, (GElement *)DeleteElement); */ for (it = gElementsInstances.begin(); it != gElementsInstances.end(); ++it) if ((*it) == Element) return G_NO_ERROR; gElementsInstances.push_back((GElement *)Element); return G_NO_ERROR; } // Remove an element reference from the proxy that created it. GError GKernel::RemoveElementReference(const GElement *Element) { if (!Element) return G_INVALID_PARAMETER; GList::iterator it; /* it = std::lower_bound(gElementsInstances.begin(), gElementsInstances.end(), DeleteElement, ElementsPtrCmp); if (it != gElementsInstances.end() && (*it) == DeleteElement) gElementsInstances.erase(it); */ for (it = gElementsInstances.begin(); it != gElementsInstances.end(); ++it) { if ((*it) == Element) { gElementsInstances.erase(it); return G_NO_ERROR; } } return G_NO_ERROR; } // Is 'InspectedType' of type (equal or derived) 'Class_ID' ? GBool GKernel::IsTypeOfType(const GClassID& InspectedType, const GClassID& Class_ID) const { GError err; GInt32 i; // Find a proxy that can create the element type. err = FindProxy(InspectedType, i); while (err == G_NO_ERROR) { if (gRegisteredProxies[i].Proxy()->ClassID() == Class_ID) return G_TRUE; // We cannot go up further more. if (gRegisteredProxies[i].Proxy()->IsRootClass()) return G_FALSE; err = FindProxy(gRegisteredProxies[i].Proxy()->DerivedClassID(), i); } return G_FALSE; } /* `Source' -- The source element. `Destination' -- The destination element. Return if operation will be succeeding: `G_NO_ERROR', else an error code. This function can fail in two cases: - Source and Destination are of the same type (same ClassID). In this case failure can result from the physical cloning operation. - Source and Destination are of different type. In this case failure can result because 2 reasons: - The Source element cannot be converted into a temporary element of the same class of Destination. Conversion is possible thanks to converter plugins. If kernel has not registered a such plugin a G_PLUGIN_NOTPRESENT error code will be returned. - The Source element can be converted into a temporary element of the same class of Destination, but cloning of temporary_object -> Destination has failed. */ GError GKernel::Copy(const GElement& Source, GElement& Destination) const { // Todo: Check if we can choose a converter plugin that support this copy. if (Source.ClassID() == Destination.ClassID()) return Destination.CopyFrom(Source); else return G_PLUGIN_NOTPRESENT; } /* `ProxyList' -- A list of proxies (well, proxies states) that manage root classes. A root class is a class that does not derive from other classes (ex: GElement). The list is first cleared, and then filled up with requested proxies. Use the Plugin Wizard Tool, you can find it in the $AMANITHDIR/tools/plugwizard directory. */ void GKernel::RootClassProxies(GDynArray& ProxyList) const { GInt32 i, j; GProxyState p; ProxyList.clear(); j = ProxiesCount(); for (i = 0; i < j; i++) { p = gRegisteredProxies[i]; if (p.Proxy()->IsRootClass()) ProxyList.push_back(p); } } /* `FatherClassID' -- A class descriptor specifying what kind of proxies we want to retrieve. `ChildList' -- A list of proxies that manage classes that derive from FatherClassID. The list is first cleared, and then filled up with requested proxies. Use Plugin Wizard Tool, you can find it into $AMANITHDIR/tools/plugwizard directory. */ void GKernel::ChildClassProxies(const GClassID& FatherClassID, GDynArray& ChildList) const { GInt32 i, j; GProxyState p; ChildList.clear(); j = ProxiesCount(); for (i = 0; i < j; i++) { p = gRegisteredProxies[i]; // we check for parent type and prevent a self classid reference if ((p.Proxy()->DerivedClassID() == FatherClassID) && (!p.Proxy()->IsRootClass())) ChildList.push_back(p); } } }