/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2007 by Systems in Motion. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. * See the file LICENSE.GPL at the root directory of this source * distribution for additional information about the GNU GPL. * * For using Coin with software that can not be combined with the GNU * GPL, and for taking advantage of the additional benefits of our * support services, please contact Systems in Motion about acquiring * a Coin Professional Edition License. * * See http://www.coin3d.org/ for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * http://www.sim.no/ sales@sim.no coin-support@coin3d.org * \**************************************************************************/ /*! \class SoNodeKitListPart SoNodeKitListPart.h Inventor/nodekits/SoNodeKitListPart.h \brief The SoNodeKitListPart class is a container node. \ingroup nodekits This node is basically just a container node with these differences versus the other group nodes: 1. It is possible to make a list of which node class types are allowed as children. 2. You can freely select which kind of group node behaviour you want this container to have. Default is to act like an SoGroup node, but by using SoNodeKitListPart::setContainerType(), you can change the behaviour to be like a separator, a switch or whatever else you have of node types inheriting SoGroup. \sa SoGroup, SoSeparator, SoSwitch */ #include #include #include #include #include #include #include #include #include #include #if COIN_DEBUG #include #endif // COIN_DEBUG static SoTypeList * sonodekitlistpart_deflist = NULL; static void sonodekitlistpart_atexit_cleanup(void) { delete sonodekitlistpart_deflist; sonodekitlistpart_deflist = NULL; } /*! \var SoChildList * SoNodeKitListPart::children \COININTERNAL */ SO_NODE_SOURCE(SoNodeKitListPart); /*! Constructor. */ SoNodeKitListPart::SoNodeKitListPart(void) { SO_NODE_INTERNAL_CONSTRUCTOR(SoNodeKitListPart); SO_NODE_ADD_FIELD(containerTypeName,(SoGroup::getClassTypeId().getName().getString())); SO_NODE_ADD_FIELD(childTypeNames,(SoNode::getClassTypeId().getName().getString())); SO_NODE_ADD_FIELD(containerNode,(NULL)); this->containerNode.setValue(new SoGroup); // to avoid notification problems (container node is in the scene // graph and notification will be propagated throught the scene // graph). If notification is enabled for this field, the trigger // path will be incorrect since it will stop at this node. this->containerNode.enableNotify(FALSE); this->containerNode.setDefault(TRUE); this->typelistlocked = FALSE; this->children = new SoChildList(this); this->children->append(this->containerNode.getValue()); } /*! Destructor. */ SoNodeKitListPart::~SoNodeKitListPart() { delete this->children; this->containerNode.setValue(NULL); } /*! Does initialization common for all objects of the SoNodeKitListPart class. This includes setting up the type system, among other things. */ void SoNodeKitListPart::initClass(void) { SO_NODE_INTERNAL_INIT_CLASS(SoNodeKitListPart, SO_FROM_INVENTOR_1); SoType type = SoNodeKitListPart::getClassTypeId(); SoAudioRenderAction::addMethod(type, SoAudioRenderAction::callDoAction); } /*! Return type id for the list container. Default is SoGroup. */ SoType SoNodeKitListPart::getContainerType(void) const { return this->containerNode.getValue()->getTypeId(); } /*! Change the behaviour of this container node. \a newContainerType must be derived from SoGroup. */ void SoNodeKitListPart::setContainerType(SoType newContainerType) { if (this->typelistlocked) { #if COIN_DEBUG SoDebugError::post("SoNodeKitListPart::setContainerType", "type list has been locked"); #endif // COIN_DEBUG return; } #if COIN_DEBUG if (!newContainerType.isDerivedFrom(SoGroup::getClassTypeId())) { SoDebugError::postInfo("SoNodeKitListPart::setContainerType", "'%s' is not a group node class type", newContainerType.getName().getString()); return; } #endif // debug SoGroup * newroot = (SoGroup *) newContainerType.createInstance(); SoChildList * kids = this->containerNode.getValue()->getChildren(); for (int i=0; i < kids->getLength(); i++) { newroot->addChild((*kids)[i]); } this->containerNode.setValue(newroot); this->children->remove(0); this->children->append(newroot); const SbName nameoftype = newContainerType.getName(); if (nameoftype != this->containerTypeName.getValue()) { this->containerTypeName.setValue(nameoftype); } } /*! Return a list of all types which are allowed as children of this node. Default is to allow all nodetypes. */ const SoTypeList & SoNodeKitListPart::getChildTypes(void) const { if (this->allowedtypes.getLength()) return this->allowedtypes; // Dynamically allocated to avoid problems on systems which doesn't // handle static constructors. if (!sonodekitlistpart_deflist) { sonodekitlistpart_deflist = new SoTypeList; sonodekitlistpart_deflist->append(SoNode::getClassTypeId()); coin_atexit((coin_atexit_f*)sonodekitlistpart_atexit_cleanup, CC_ATEXIT_NORMAL); } return *sonodekitlistpart_deflist; } /*! Add one more node class type which should be allowed to be used in the node list. Note: the default single SoNode type in the list will be removed upon the first call to this method. */ void SoNodeKitListPart::addChildType(SoType typeToAdd) { if (this->typelistlocked) { #if COIN_DEBUG SoDebugError::post("SoNodeKitListPart::addChildType", "type list has been locked"); #endif // COIN_DEBUG return; } for (int i=0; i < this->allowedtypes.getLength(); i++) { if (this->allowedtypes[i] == typeToAdd) { #if COIN_DEBUG SoDebugError::post("SoNodeKitListPart::addChildType", "type '%s' already present in the list", typeToAdd.getName().getString()); #endif // COIN_DEBUG return; } } this->childTypeNames.set1Value(this->allowedtypes.getLength(), typeToAdd.getName()); this->allowedtypes.append(typeToAdd); } /*! Return \c TRUE if nodes of the \a typeToCheck class type can be added to the list. */ SbBool SoNodeKitListPart::isTypePermitted(SoType typeToCheck) const { int numtypes = this->allowedtypes.getLength(); if ((numtypes == 0) && typeToCheck.isDerivedFrom(SoNode::getClassTypeId())) return TRUE; for (int i=0; i < numtypes; i++) { if (typeToCheck.isDerivedFrom(this->allowedtypes[i])) return TRUE; } return FALSE; } /*! Return \c TRUE if \a child has a class type which is permitted to be in the list. */ SbBool SoNodeKitListPart::isChildPermitted(const SoNode * child) const { return this->isTypePermitted(child->getTypeId()); } /*! Calls set() on the container node with \a fieldDataString. */ void SoNodeKitListPart::containerSet(const char * fieldDataString) { this->containerNode.getValue()->set(fieldDataString); } /*! Lock type list so no more node class types can be added by using addChildType(), and locks container type so it no longer can be changed by setContainerType(). */ void SoNodeKitListPart::lockTypes(void) { this->typelistlocked = TRUE; } /*! Returns \c TRUE if the list of allowable node types and the container type have both been locked. */ SbBool SoNodeKitListPart::isTypeLocked(void) const { return this->typelistlocked; } /*! Adds \a child to the internal list of children, if it is of a type permitted to be added. */ void SoNodeKitListPart::addChild(SoNode * child) { if (!this->isChildPermitted(child)) { #if COIN_DEBUG SoDebugError::post("SoNodeKitListPart::addChild", "can't add child of type '%s'", child->getTypeId().getName().getString()); #endif // COIN_DEBUG return; } ((SoGroup *) this->containerNode.getValue())->addChild(child); } /*! Inserts \a child in the internal list of children at position \c childIndex, if it is of a type permitted to be added. */ void SoNodeKitListPart::insertChild(SoNode * child, int childIndex) { if (!this->isChildPermitted(child)) { #if COIN_DEBUG SoDebugError::post("SoNodeKitListPart::insertChild", "can't insert child of type '%s'", child->getTypeId().getName().getString()); #endif // COIN_DEBUG return; } ((SoGroup *) this->containerNode.getValue())->insertChild(child, childIndex); } /*! Return child node at position \a index. */ SoNode * SoNodeKitListPart::getChild(int index) const { return ((SoGroup *) this->containerNode.getValue())->getChild(index); } /*! Return position index of \a child in list of children. */ int SoNodeKitListPart::findChild(SoNode * child) const { return ((SoGroup *) this->containerNode.getValue())->findChild(child); } /*! Return number of children. */ int SoNodeKitListPart::getNumChildren(void) const { return ((SoGroup *) this->containerNode.getValue())->getNumChildren(); } /*! Remove child at position \a index. */ void SoNodeKitListPart::removeChild(int index) { ((SoGroup *) this->containerNode.getValue())->removeChild(index); } /*! Remove \a child from list of children. */ void SoNodeKitListPart::removeChild(SoNode * child) { ((SoGroup *) this->containerNode.getValue())->removeChild(child); } /*! Replace child at \a index with \a newChild, if \a newChild is of a permitted type. */ void SoNodeKitListPart::replaceChild(int index, SoNode * newChild) { if (!this->isChildPermitted(newChild)) { #if COIN_DEBUG SoDebugError::post("SoNodeKitListPart::replaceChild", "can't replace with child of type '%s'", newChild->getTypeId().getName().getString()); #endif // COIN_DEBUG return; } ((SoGroup *) this->containerNode.getValue())->replaceChild(index, newChild); } /*! Replace \a oldChild with \a newChild, if \a newChild is of a permitted type. */ void SoNodeKitListPart::replaceChild(SoNode * oldChild, SoNode * newChild) { if (!this->isChildPermitted(newChild)) { #if COIN_DEBUG SoDebugError::post("SoNodeKitListPart::replaceChild", "can't replace with child of type '%s'", newChild->getTypeId().getName().getString()); #endif // COIN_DEBUG return; } ((SoGroup *) this->containerNode.getValue())->replaceChild(oldChild, newChild); } // Documented in superclass. SbBool SoNodeKitListPart::affectsState(void) const { // Just "forwards" the call to the same method at the container // node. return this->containerNode.getValue()->affectsState(); } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::doAction(SoAction * action) { int numindices; const int * indices; if (action->getPathCode(numindices, indices) == SoAction::IN_PATH) { children->traverseInPath(action, numindices, indices); } else { this->children->traverse(action); } } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::callback(SoCallbackAction * action) { SoNodeKitListPart::doAction((SoAction*)action); } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::GLRender(SoGLRenderAction * action) { SoNodeKitListPart::doAction((SoAction*)action); } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::getBoundingBox(SoGetBoundingBoxAction * action) { int numindices; const int * indices; int last = action->getPathCode(numindices, indices) == SoAction::IN_PATH ? indices[numindices-1] : this->children->getLength() - 1; SbVec3f acccenter(0.0f, 0.0f, 0.0f); int numacc = 0; for (int i = 0; i <= last; i++) { children->traverse(action, i, i); if (action->isCenterSet()) { acccenter += action->getCenter(); numacc++; action->resetCenter(); } } if (numacc) action->setCenter(acccenter / float(numacc), FALSE); } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::getMatrix(SoGetMatrixAction * action) { int numindices; const int * indices; switch (action->getPathCode(numindices, indices)) { case SoAction::IN_PATH: this->children->traverseInPath(action, numindices, indices); break; case SoAction::OFF_PATH: this->children->traverse(action); break; case SoAction::NO_PATH: case SoAction::BELOW_PATH: break; default: assert(0 && "unknown path code"); } } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::handleEvent(SoHandleEventAction * action) { SoNodeKitListPart::doAction((SoAction*)action); } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::pick(SoPickAction * action) { SoNodeKitListPart::doAction((SoAction*)action); } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::search(SoSearchAction * action) { SoNode::search(action); if (!action->isFound()) SoNodeKitListPart::doAction(action); } /*! This just "forwards" the call to the same method at the container node. */ void SoNodeKitListPart::getPrimitiveCount(SoGetPrimitiveCountAction * action) { SoNodeKitListPart::doAction((SoAction*)action); } /*! This just "forwards" the call to the same method at the container node. */ SoChildList * SoNodeKitListPart::getChildren(void) const { return this->children; } /*! Return the SoGroup container which is the parent of all the children which has been added. */ SoGroup * SoNodeKitListPart::getContainerNode(void) { return (SoGroup *) this->containerNode.getValue(); } // Documented in superclass. Overrides this method to set up internal // data according to what is contained within the imported field // values. SbBool SoNodeKitListPart::readInstance(SoInput * in, unsigned short flags) { if (inherited::readInstance(in, flags)) { this->syncInternalData(); return TRUE; } return FALSE; } // Documented in superclass. Overrides this method to set up internal // data according to what is contained within the copied field values. void SoNodeKitListPart::copyContents(const SoFieldContainer * fromFC, SbBool copyConnections) { inherited::copyContents(fromFC, copyConnections); this->syncInternalData(); } /*! Synchronize internal data with the contents of the fields. */ void SoNodeKitListPart::syncInternalData(void) { // Set up type list. this->allowedtypes.truncate(0); for (int i=0; i < this->childTypeNames.getNum(); i++) { this->allowedtypes.append(SoType::fromName(this->childTypeNames[i])); } // Set up container node, if necessary. if (this->containerNode.getValue() == NULL) { SoType containerType = SoType::fromName(this->containerTypeName.getValue()); this->containerNode.setValue((SoNode*)containerType.createInstance()); this->containerNode.setDefault(TRUE); } if (this->children->getLength() == 0) { this->children->append(this->containerNode.getValue()); } else if ((*this->children)[0] != this->containerNode.getValue()) { this->children->remove(0); this->children->append(this->containerNode.getValue()); } } // TRUE if default child can be created SbBool SoNodeKitListPart::canCreateDefaultChild(void) const { return this->getDefaultChildType() != SoType::badType(); } // create, add and return a new default child. Should not be called if default // child can not be created. SoNode * SoNodeKitListPart::createAndAddDefaultChild(void) { SoType type = this->getDefaultChildType(); assert(type != SoType::badType()); SoNode * newnode = (SoNode*) type.createInstance(); this->addChild(newnode); return newnode; } // return the first non abstract child type, or SoType::badType if none exists SoType SoNodeKitListPart::getDefaultChildType(void) const { const SoTypeList & typelist = this->getChildTypes(); int n = typelist.getLength(); for (int i = 0; i < n; i++) { if (typelist[i].canCreateInstance()) return typelist[i]; } return SoType::badType(); }