File: [Development] / inventor / lib / nodekits / src / nodekits / SoBaseKit.c++ (download)
Revision 1.2, Fri Oct 13 20:28:00 2000 UTC (17 years ago) by jlim
Branch: MAIN
CVS Tags: release-2_1_5-9, release-2_1_5-8, release-2_1_5-10, HEAD Changes since 1.1: +4 -4
lines
Fix for Bug 25 and conform to ANSI 'for' scoping rules.
|
/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
* Copyright (C) 1990,91 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.2 $
|
| Classes:
| SoBaseKit
|
| Author(s) : Paul Isaacs and Thad Beier
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <Inventor/SbBox.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoNodeKitPath.h>
#include <Inventor/SoPath.h>
#include <Inventor/SoPickedPoint.h>
#include <Inventor/actions/SoCallbackAction.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/actions/SoGetBoundingBoxAction.h>
#include <Inventor/actions/SoGetMatrixAction.h>
#include <Inventor/actions/SoHandleEventAction.h>
#include <Inventor/actions/SoRayPickAction.h>
#include <Inventor/actions/SoSearchAction.h>
#include <Inventor/actions/SoWriteAction.h>
#include <Inventor/details/SoNodeKitDetail.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/errors/SoReadError.h>
#include <Inventor/misc/SoAuditorList.h>
#include <Inventor/nodekits/SoBaseKit.h>
#include <Inventor/nodekits/SoNodeKitListPart.h>
#include <Inventor/nodes/SoCallback.h>
#include <Inventor/nodes/SoEventCallback.h>
#include <Inventor/nodes/SoResetTransform.h>
#include <Inventor/nodes/SoSeparator.h>
#include <SoDebug.h>
#include <stdlib.h>
#include <ctype.h>
// Defines for printing out file data for nodekits
#define SO_BASEKIT_FILEDATA_HEADER "partName\/childNum pairs [ "
#define SO_BASEKIT_FILEDATA_FOOTER "]"
SO_KIT_SOURCE(SoBaseKit);
SbBool SoBaseKit::searchingChildren = FALSE;
////////////////////////////////////////////////////////////////////////
//
// Description:
// This prints a diagram of the parts in this class
//
// Use: static, internal
void SoBaseKit::printDiagram()
{
const SoNodekitCatalog *cat = getNodekitCatalog();
fprintf( stdout, "CLASS So%s\n", getTypeId().getName().getString() );
printSubDiagram( cat->getName(0), 0 );
}
void SoBaseKit::printSubDiagram( const SbName &rootName, int level )
{
const SoNodekitCatalog *cat = getNodekitCatalog();
// If part is new for this class, or if part is different in this
// class than in parent class, print "-->" at the beginning of
// the line. Else, 3 spaces
SbBool sameAsInParentClass = TRUE;
if ( getTypeId() == SoBaseKit::getClassTypeId() )
sameAsInParentClass = FALSE;
else {
SoType parentClassType = getTypeId().getParent();
SoBaseKit *parentClassInst =
(SoBaseKit *)parentClassType.createInstance();
const SoNodekitCatalog *parentClassCat;
parentClassCat = parentClassInst->getNodekitCatalog();
parentClassInst->ref();
parentClassInst->unref();
int parentClassPartNum;
parentClassPartNum = parentClassCat->getPartNumber( rootName );
if ( parentClassPartNum == SO_CATALOG_NAME_NOT_FOUND ) {
sameAsInParentClass = FALSE;
}
else {
if ( cat->getType( rootName ) !=
parentClassCat->getType( rootName ) )
sameAsInParentClass = FALSE;
if ( cat->getDefaultType( rootName ) !=
parentClassCat->getDefaultType( rootName ) )
sameAsInParentClass = FALSE;
}
}
if ( sameAsInParentClass )
fprintf( stdout, " ");
else
fprintf( stdout, "-->");
// indent
int i;
for ( i = 0; i < level; i++ )
fprintf( stdout, " ");
fprintf( stdout, "\"%s\"\n", rootName.getString());
level++;
int howMany = cat->getNumEntries();
int *inds = new int[howMany];
// Find the children parts, going from right to left, and load
// their indices into the inds array.
SbName rightSib = "";
int curInd;
SbBool foundIt;
for ( curInd = 0, foundIt = TRUE; foundIt == TRUE; ) {
// Find the part with right sibling named RightSib
foundIt = FALSE;
for ( i = 0; i < cat->getNumEntries(); i++ ) {
if ( foundIt == FALSE && cat->getParentName(i) == rootName ) {
if (cat->getRightSiblingName(i) == rightSib ) {
inds[curInd] = i;
rightSib = cat->getName(i);
foundIt = TRUE;
curInd++;
}
}
}
}
for (i = curInd - 1; i >= 0; i-- ) {
printSubDiagram( cat->getName(inds[i]), level );
}
delete [ /*howMany*/ ] inds;
}
void SoBaseKit::printTable()
{
const SoNodekitCatalog *cat = getNodekitCatalog();
fprintf( stdout, "CLASS So%s\n", getTypeId().getName().getString() );
for (int i = 0; i < cat->getNumEntries(); i++ ) {
if ( cat->isPublic(i) == FALSE )
fprintf(stdout, "PVT ");
else
fprintf(stdout, " ");
const char *catName = cat->getName(i).getString();
fprintf( stdout, " \"%s\", So%s", catName,
cat->getType(i).getName().getString());
if ( cat->isList(i) == TRUE ) {
const SoTypeList l = cat->getListItemTypes(i);
fprintf(stdout, " [");
for (int j = 0; j < l.getLength(); j++ ) {
if ( j > 0 )
fprintf(stdout, ",");
fprintf(stdout, " So%s", l[j].getName().getString() );
}
fprintf(stdout, " ] ");
}
else {
fprintf(stdout, " --- ");
}
if ( cat->getType(i) != cat->getDefaultType(i) ) {
fprintf(stdout, ", (default type = So%s)",
cat->getDefaultType(i).getName().getString());
}
fprintf(stdout, "\n");
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor
//
// Use: public
SoBaseKit::SoBaseKit()
//
////////////////////////////////////////////////////////////////////////
{
children = new SoChildList(this);
fieldDataForWriting = NULL;
// We use the NODE constructor
SO_KIT_CONSTRUCTOR(SoBaseKit);
isBuiltIn = TRUE;
// Create the Catalog entry for "this"
// Don't use the macro, because we don't create a field for this part.
if (firstInstance && !nodekitCatalog->addEntry("this",
SoBaseKit::getClassTypeId(), SoBaseKit::getClassTypeId(),
TRUE, "", "", FALSE, SoType::badType(), SoType::badType(), FALSE ))
catalogError();
SO_KIT_ADD_CATALOG_LIST_ENTRY(callbackList, SoSeparator, TRUE,
this, , SoCallback, TRUE );
SO_KIT_ADD_LIST_ITEM_TYPE(callbackList, SoEventCallback );
if ( getNodekitCatalog() != NULL )
nodekitPartsList = new SoNodekitParts( this );
connectionsSetUp = FALSE;
setUpConnections( TRUE, TRUE );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor (necessary since inline destructor is too complex)
//
// Use: public
SoBaseKit::~SoBaseKit()
//
////////////////////////////////////////////////////////////////////////
{
if (fieldDataForWriting != NULL)
delete fieldDataForWriting;
if ( nodekitPartsList != NULL )
delete nodekitPartsList;
delete children;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns the containerNode within the SoNodeKitListPart given
// by listName.
//
// Use: protected
SoGroup *
SoBaseKit::getContainerNode( const SbName &listName, SbBool makeIfNeeded )
{
SoNodeKitListPart *l
= (SoNodeKitListPart *) getAnyPart( listName, makeIfNeeded );
if ( l == NULL )
return NULL;
return ( l->getContainerNode() );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Return the part with the given name
//
// Use: protected
SoNode *
SoBaseKit::getAnyPart( const SbName &partName, SbBool makeIfNeeded,
SbBool leafCheck, SbBool publicCheck )
//
////////////////////////////////////////////////////////////////////////
{
return (nodekitPartsList->getAnyPart( partName, makeIfNeeded,
leafCheck, publicCheck ));
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Return the part with the given name
//
// Use: public
SoNode *
SoBaseKit::getPart( const SbName &partName, SbBool makeIfNeeded )
//
////////////////////////////////////////////////////////////////////////
{
// the fourth argument, leafCheck and publicCheck are TRUE, because we
// don't ordinarily return parts unless they are public leaves.
return ( getAnyPart( partName, makeIfNeeded, TRUE, TRUE ) );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Given a node or a path to a node, sees if it's a part in the
// nodekit, a part in a nested nodekit, or an element of a list part.
// If so, returns the name. If not, returns the empty string.
//
// Use: public
SbString
SoBaseKit::getPartString( const SoBase *part )
//
////////////////////////////////////////////////////////////////////////
{
const SoNodekitParts *partsList = getNodekitPartsList();
const SoNodekitCatalog *cat = getNodekitCatalog();
if ( part == NULL)
return "";
if ( part->isOfType( SoNode::getClassTypeId() )) {
// trivial case
if ( part == this )
return "this";
// Look through the parts list and see if you can find a match.
// Remember to skip entry 0, which is 'this'.
for ( int i = 1; i < partsList->numEntries; i++ ) {
SoNode *iPart = partsList->fieldList[i]->getValue();
if (iPart != NULL) {
// Simple match
if ( iPart == part )
return cat->getName(i).getString();
if (iPart->isOfType( SoBaseKit::getClassTypeId())) {
// Try to look inside the nodekit part.
SoBaseKit *kit = (SoBaseKit *) iPart;
SbString subString = kit->getPartString(part);
if (subString != "") {
SbString answer( cat->getName(i).getString() );
answer += ".";
answer += subString.getString();
return answer;
}
}
else if ( cat->isList(i) ) {
// Within a leaf that's a list part. Try to look inside.
SoNodeKitListPart *lst = (SoNodeKitListPart *) iPart;
for (int indx = 0;
indx < lst->getNumChildren(); indx++ ) {
SoNode *kid = lst->getChild(indx);
if (kid == part) {
char indxString[30];
sprintf(indxString, "[%d]", indx );
SbString answer( cat->getName(i).getString() );
answer += indxString;
return answer;
}
else if (kid->isOfType(SoBaseKit::getClassTypeId())) {
// Try to look inside the nodekit part.
SbString subString
= ((SoBaseKit *)kid)->getPartString(part);
if (subString != "") {
char indxString[30];
sprintf(indxString, "[%d]", indx );
SbString answer( cat->getName(i).getString() );
answer += indxString;
answer += ".";
answer += subString.getString();
return answer;
}
}
}
}
}
}
}
else if (part->isOfType( SoPath::getClassTypeId() )) {
const SoFullPath *fullP = (const SoFullPath *) part;
int pathIndex;
// First, find 'this' on the path.
for (pathIndex = 0; pathIndex < fullP->getLength(); pathIndex++ ) {
if ( fullP->getNode(pathIndex) == this )
break;
}
// If 'this' is not on path...
if ( pathIndex >= fullP->getLength() )
return "";
// If 'this' is at end of path...
if ( pathIndex == fullP->getLength() - 1 )
return "this";
pathIndex++;
// This node appears on the path, but is not the tail.
// See if the tail is named within this node. Remember to skip
// entry 0, which is 'this'.
for ( int i = 1; i < partsList->numEntries; i++ ) {
SoNode *iPart = partsList->fieldList[i]->getValue();
// If this part lies on the path, then we have work to do...
if (iPart == fullP->getNode(pathIndex)) {
// Is this part at the end of the path?
if ( pathIndex == fullP->getLength() - 1 )
return cat->getName(i).getString();
// Is this part a leaf in the catalog?
if ( cat->isLeaf(i) ) {
if (iPart->isOfType( SoBaseKit::getClassTypeId())) {
// Try to look inside the nodekit part.
SoBaseKit *kit = (SoBaseKit *) iPart;
SbString subString = kit->getPartString(part);
if (subString != "") {
SbString answer( cat->getName(i).getString() );
answer += ".";
answer += subString.getString();
return answer;
}
}
else if ( cat->isList(i) ) {
SoNodeKitListPart *lst = (SoNodeKitListPart *) iPart;
for (int indx = 0;
indx < lst->getNumChildren(); indx++ ) {
SoNode *kid = lst->getChild(indx);
if (kid == fullP->getTail()) {
char indxString[30];
sprintf(indxString, "[%d]", indx );
SbString answer( cat->getName(i).getString() );
answer += indxString;
return answer;
}
else if (kid->isOfType(SoBaseKit::getClassTypeId())) {
// Try to look inside the nodekit part.
SbString subString
= ((SoBaseKit *)kid)->getPartString(part);
if (subString != "") {
char indxString[30];
sprintf(indxString, "[%d]", indx );
SbString answer( cat->getName(i).getString() );
answer += indxString;
answer += ".";
answer += subString.getString();
return answer;
}
}
}
}
}
// Otherwise, increment the pathIndex and.
// Keep on looking through the catalog...
// We can keep using the same loop, since
// successive entries in a path should always proceed to
// increasing indices in the parts list. This is because
// when classes define catalogs, entries must be added as
// children of pre-existing parts.
pathIndex++;
}
}
}
// we didn't find a match...
return "";
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns a path that begins with 'this', and ends at
// the part named.
//
// If 'pathToExtend' is not NULL:
// the returned path will be a copy of 'pathToExtend' with the new
// path appended to it. In order to append, however, the following
// condition must be met:
// 'this' must lie on the pathToExtend, otherwise NULL is returned.
// If 'this' lies on pathToExtend, then a copy is made, and the
// copy is truncated to end at 'this.' Finally, the path from 'this'
// down to the part will be appended.
//
//
// Use: public
SoNodeKitPath *
SoBaseKit::createPathToAnyPart(const SbName &partName,
SbBool makeIfNeeded, SbBool leafCheck,
SbBool publicCheck, const SoPath *pathToExtend )
//
////////////////////////////////////////////////////////////////////////
{
// Return if pathToExtend is non-NULL but doesn't contain 'this'
if ( pathToExtend != NULL &&
((const SoFullPath *)pathToExtend)->containsNode(this) == FALSE ) {
#ifdef DEBUG
SoDebugError::post("SoBaseKit::createPathToAnyPart",
"The given pathToExtend does not contain this node.Returning NULL");
#endif
return NULL;
}
SoFullPath *thePath = nodekitPartsList->createPathToAnyPart( partName,
makeIfNeeded, leafCheck, publicCheck );
if ( thePath == NULL )
return NULL;
if ( pathToExtend == NULL )
return (SoNodeKitPath *) thePath;
const SoFullPath *fullPathToExtend = (const SoFullPath *) pathToExtend;
thePath->ref();
fullPathToExtend->ref();
// Create a copy of 'fullPathToExtend' with 'thePath' tacked onto it
// First, copy fullPathToExtend into longPath
SoFullPath *longPath = (SoFullPath *) fullPathToExtend->copy();
longPath->ref();
// Now, truncate longPath to end at 'this'
while ( longPath->getTail() != this )
longPath->pop();
// Finally, append 'thePath' after 'longPath'. Leave out thePath->head(),
// since it's already at the tail of longPath...
for( int i = 1; i < thePath->getLength(); i++ )
longPath->append( thePath->getIndex( i ) );
thePath->unref();
fullPathToExtend->unref();
longPath->unrefNoDelete();
return (SoNodeKitPath *)longPath;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns a path that begins with 'this', and ends at
// the part named.
//
// If 'pathToExtend' is not NULL:
// the returned path will be a copy
// of 'pathToExtend' with the new path appended to it. In order to
// append, however, the following condition must be met:
// pathToExtend->getTail() must equal 'this',
// otherwise NULL will be returned.
//
//
// Use: public
SoNodeKitPath *
SoBaseKit::createPathToPart(const SbName &partName,
SbBool makeIfNeeded, const SoPath *pathToExtend )
//
////////////////////////////////////////////////////////////////////////
{
// the fourth and fifth arguments, leafCheck and publicCheck, are TRUE,
// because we don't ordinarily return parts unless they are public leaves.
return ( (SoNodeKitPath *) createPathToAnyPart( partName, makeIfNeeded,
TRUE, TRUE, pathToExtend ) );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Return the part with the given name
//
// Use: public
SbBool
SoBaseKit::setPart( const SbName &partName, SoNode *from )
//
////////////////////////////////////////////////////////////////////////
{
// the third argument, anyPart, is FALSE, because we don't ordinarily
// return parts unless they are public leaves.
return ( setAnyPart( partName, from, FALSE ) );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Return the part with the given name
//
// Use: protected
SbBool
SoBaseKit::setAnyPart( const SbName &partName, SoNode *from, SbBool anyPart )
//
////////////////////////////////////////////////////////////////////////
{
if ( from )
from->ref();
SbBool answer = nodekitPartsList->setAnyPart( partName, from, anyPart );
if ( from )
from->unref();
return answer;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets field values in the desired parts of the nodeKit...
// The argument string contains a list of desiredNodeName/fieldData
// string pairs. The string is parsed, and subsequent calls are made
// to: theNode = getPart( desiredNodeName);
// and: theNode->set( fieldData );
//
// Use: public
SbBool
SoBaseKit::set(char *nameValuePairListString)
// the string to use in setting the values
//
////////////////////////////////////////////////////////////////////////
{
char *string = strdup( nameValuePairListString );
char *stringP = string;
char *c;
SbBool success = TRUE;
SoNode *node;
while(*string) {
skipWhiteSpace(string);
if(!*string)
break;
// look for the node name
c = string;
while(*c && !isspace(*c) && *c != '{')
c++;
if(!*c) // no more data, return
break;
int length = c - string + 1;
char *desiredNodeName = new char [length];
strncpy(desiredNodeName, string, c - string);
desiredNodeName[c - string] = 0;
string = c;
// find that node
node = getPart(desiredNodeName, TRUE);
delete [ /*length*/ ] desiredNodeName;
if (node == NULL) // no node found, return
break;
// get the field data
skipWhiteSpace(string);
if(*string != '{') {
#ifdef DEBUG
SoDebugError::post("SoBaseKit::set",
"Found char '%c' in string \"%s\", expected '{'",
*string, nameValuePairListString);
#endif
success = FALSE;
break;
}
string++;
c = string;
while(*c && *c != '}')
c++;
length = c - string + 1;
char *fielddata = new char [length];
strncpy(fielddata, string, c - string);
fielddata[c - string] = 0;
string = c + 1;
// call the SoNode::set method to set the fields
success &= node->set(fielddata);
delete [ /*length*/ ] fielddata;
}
free(stringP);
return success;
}
#define __SO_COMMENT_CHAR '#'
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets field values in the desired parts of the nodeKit...
// The partNameString contains the name of a part to set values for.
// It may be of the form 'part1.part2.part3', so that the kit will
// search within it's template for the node.
// Next, calls the node's set, using parameterString as the input...
//
// Use: public
SbBool
SoBaseKit::set(char *partNameString, // name of the part
char *parameterString ) // values for the part
//
////////////////////////////////////////////////////////////////////////
{
SoNode *node = getPart(partNameString, TRUE);
// call the SoNode::set method to set the fields
if ( node == NULL )
return FALSE;
if ( node->set(parameterString) )
return TRUE;
else
return FALSE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Creates the nodekitPartsList list.
// Should be called once during the constructor for each instance.
//
//
//
// Use: protected
void
SoBaseKit::createNodekitPartsList()
//
////////////////////////////////////////////////////////////////////////
{
if ( getNodekitCatalog() != NULL ) {
if (nodekitPartsList)
delete nodekitPartsList;
nodekitPartsList = new SoNodekitParts( this );
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Creates the parts in this nodekit for which
// nullByDefault is FALSE in the nodekitCatalog
//
// Use: protected
void
SoBaseKit::createDefaultParts()
//
////////////////////////////////////////////////////////////////////////
{
const SoNodekitCatalog *cat = getNodekitCatalog();
SoSFNode *theField;
SoNode *oldPart, *myPart;
// Start at index 1. Don't want to do anything to 'this', index 0
for ( int i = 1; i < cat->getNumEntries(); i++ ) {
const SbName pName = cat->getName(i);
theField = nodekitPartsList->fieldList[i];
// If part is created by default and either
// [a] hasn't been created yet
// or [b] has been created, but the default type has changed...
if (cat->isNullByDefault(i) == FALSE ) {
oldPart = theField->getValue();
if ( oldPart == NULL
|| oldPart->isOfType( cat->getDefaultType(i) ) == FALSE ) {
myPart = (SoNode *) cat->getDefaultType(i).createInstance();
setAnyPart( cat->getName(i) , myPart );
}
}
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Skips white space in strings, used by the set method.
//
// Use: private
void
SoBaseKit::skipWhiteSpace(char *&string)
//
////////////////////////////////////////////////////////////////////////
{
// Keep going while space and comments appear
// Skip over space characters
while (*string && isspace(*string))
string++;
// If next character is comment character, flush until end of line
if (*string == __SO_COMMENT_CHAR) {
while (*string)
string++;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Skips white space in strings, used by the set method.
//
// Use: protected
void
SoBaseKit::catalogError()
//
////////////////////////////////////////////////////////////////////////
{
#ifdef DEBUG
SoDebugError::post("SoBaseKit::catalogError",
"ERROR creating nodekit catalog for class %s Did you remember to call initClass?",
getTypeId().getName().getString() );
#endif
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// detach/attach any sensors, callbacks, and/or field connections.
// Called by: start/end of SoBaseKit::readInstance
// and on new copy by: start/end of SoBaseKit::copy.
// Classes that redefine must call setUpConnections(TRUE,TRUE)
// at end of constructor.
// Returns the state of the node when this was called.
//
SbBool
SoBaseKit::setUpConnections( SbBool onOff, SbBool doItAlways )
{
if ( !doItAlways && connectionsSetUp == onOff)
return onOff;
return !(connectionsSetUp = onOff);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads the fields for the nodekit.
// Then it sets the parts in the kit based on the field values.
// Also, is careful to keep any pre-defined nodes that are not
// given values in the file.
//
// Use: protected
SbBool
SoBaseKit::readInstance( SoInput *in, unsigned short /*flags*/ )
//
////////////////////////////////////////////////////////////////////////
{
SbBool readOK = TRUE;
// [0] First, turn off notification and connections for this node
SbBool saveNotify = enableNotify(FALSE);
SbBool wasSetUp = setUpConnections( FALSE );
// [1] make a temporary list of all the part fields.
int numParts = nodekitPartsList->numEntries;
// firstPartField is 1. Part 0 is 'this' and is not treated here.
int firstPartField = 1;
SoSFNode **realPartFldLst = nodekitPartsList->fieldList;
SoSFNode **tempPartFldLst = new ( SoSFNode *[numParts] );
int i;
for ( i = firstPartField; i < numParts; i++ ) {
tempPartFldLst[i] = new SoSFNode;
tempPartFldLst[i]->setValue( realPartFldLst[i]->getValue() );
tempPartFldLst[i]->setDefault( realPartFldLst[i]->isDefault() );
}
// [2] clear out the child list (remove all hidden children)
for ( i = getNumChildren() - 1; i >= 0; i-- )
removeChild( i );
// [3] clear out the real part fields (remember, we've made a copy!)
for ( i = firstPartField; i < numParts; i++ ) {
realPartFldLst[i]->setValue( NULL );
realPartFldLst[i]->setDefault( TRUE );
}
// [4] Try to read the fields. The unknownFieldData will contain
// any fields that are not listed in this catalog. We will try
// to use them later, with setPart(). For example, the
// field
// appearance.material Material { diffuseColor 1 0 0 }
// or simply
// material Material { diffuseColor 1 0 0 }
// will be readable by an SoSeparatorKit, even though neither
// "material" nor "appearance.material" appears as a name in our
// fieldData.
SoFieldData *unknownFieldData = new SoFieldData;
if ( !readMyFields(in, unknownFieldData ) )
readOK = FALSE;
// [5] copy any values that were read from file into
// the tempPartFldLst. These new values should replace old ones,
// since things in the file take precedence. However, if nothing
// was said about the part in file, we keep the old value in
// tempPartFldLst.
if ( readOK == TRUE ) {
for ( i = firstPartField; i < numParts; i++ ) {
if ( realPartFldLst[i]->isDefault() == FALSE )
tempPartFldLst[i]->setValue( realPartFldLst[i]->getValue() );
}
}
// [6] At this point, all the parts we want to keep are stored in the
// tempPartFldLst.
// Clear out the child list and the realPartFldLst. This readies
// us to set values from every part listed in tempPartFldLst.
if ( readOK == TRUE ) {
for ( i = getNumChildren() - 1; i >= 0; i-- )
removeChild( i );
for ( i = firstPartField; i < numParts; i++ ) {
realPartFldLst[i]->setValue(NULL);
realPartFldLst[i]->setDefault(TRUE);
}
}
// [7] call setPart() for every non-NULL or non-default part in the saved
// part list. (must call if value == NULL but isDefault() == FALSE,
// because we may get rid of some ancestors that way...)
// Then, our hidden children and part fields will be consistent.
// [7a] Skip part 0, because part 0 is "this"
// [7c] For non-leaves, remove all children.
// They will be added back when (and if) setPart() is
// called on the child.
// [7d] Set the part, using setPartFromThisCatalog()
// [7e] Copy EVERY default flag from tempPartFldLst to
// realPartFldLst. Sometimes, a NULL part has
// (default == FALSE).
if ( readOK == TRUE ) {
const SoNodekitCatalog *cat = getNodekitCatalog();
// [7a] Skip part 0, because part 0 is "this"
for ( i = firstPartField; i < numParts; i++ ) {
if ( tempPartFldLst[i]->getValue() != NULL ||
tempPartFldLst[i]->isDefault() == FALSE) {
SoNode *n = tempPartFldLst[i]->getValue();
if (n)
n->ref();
// [7c] For non-leaves, remove all children.
// They will be added back when (and if) setPart() is
// called on the child.
if (n && cat->isLeaf(i) == FALSE ) {
SoGroup *g = (SoGroup *) n;
for (int j = g->getNumChildren()-1; j>=0; j-- )
g->removeChild(j);
}
// [7d] Set the part, using setPartFromThisCatalog()
nodekitPartsList->setPartFromThisCatalog( i, n, TRUE );
if (n)
n->unref();
}
// [7e] Copy EVERY default flag from tempPartFldLst to
// realPartFldLst. Sometimes, a NULL part has
// (default == FALSE).
realPartFldLst[i]->setDefault( tempPartFldLst[i]->isDefault());
}
}
// [8] Now attempt to put the unknown fields as parts, using setPart.
if (readOK == TRUE ) {
// Now attempt to put the unknown fields as parts, using setAnyPart.
SbName partName;
SoNode *pNode;
SoSFNode *pNodeField;
for ( i = 0; i < unknownFieldData->getNumFields(); i++ ) {
partName = unknownFieldData->getFieldName(i);
pNodeField = (SoSFNode *) unknownFieldData->getField(this,i);
pNode = pNodeField->getValue();
setAnyPart( partName, pNode );
}
}
// [9] delete the temporary storage.
// Each entry in unknownFieldData contains an SoSFField that we
// created on the fly to point to a part that was then unknown.
// We need to delete these fields. Note that, usually, one does
// not go inside fieldData and delete the fields. This is because the
// 'real owner' of the field is usually a node, and the fieldData just
// stores an extra pointer. In this case, unknownFieldData is the
// owner of the field.
for ( i = 0; i < unknownFieldData->getNumFields(); i++ ) {
SoSFNode *byebye = (SoSFNode *) unknownFieldData->getField(this,i);
delete byebye;
}
delete unknownFieldData;
for ( i = firstPartField; i < numParts; i++ ) {
delete tempPartFldLst[i];
}
delete [] tempPartFldLst;
// Turn connections and notification back on for node
setUpConnections( wasSetUp );
enableNotify(saveNotify);
return readOK;
}
SbBool
SoBaseKit::readMyFields(SoInput *in, SoFieldData *&unknownFieldData )
{
const SoFieldData *fieldData = getFieldData();
// If the file is binary, then no special stuff...
if ( in->isBinary() ) {
SbBool notBuiltIn; // Not used
return fieldData->read(in, this, TRUE, notBuiltIn);
}
// Otherwise, we have special code to read fields.
// It allows for 'parts within parts.' If the fieldName is not
// recognizable, then we assume it's the name of an embedded part and
// go ahead reading the node which follows. Later, we will call setPart
// to insert it down below.
// The code below is ripped off and modified from SoFieldData::read()
// But instead of looking only for names that are valid identifiers,
// we allow any string not beginning with '}'
// Only check for "fields" or "inputs" the first time:
SbBool firstTime = TRUE;
SbName fieldName;
// Keep reading fields until done
while (TRUE) {
// Read first character - if none, EOF
char nextChar;
if (!in->read( nextChar ))
return FALSE;
// See if its a close bracket, then put the character back.
SbBool closeBracket = (nextChar == '}');
in->putBack(nextChar);
// If it was a closeBracket, we are done here.
if (closeBracket)
return TRUE;
// Otherwise, read the next name, but don't require an
// identifier. That way, we can get partNames like
// duck.foot.toe[4] to read in.
if (! in->read(fieldName, FALSE) || !fieldName)
return TRUE;
// Read field descriptions. Field descriptions may be
// given for built-in nodes, and do NOT have to be given
// for non-built-in nodes as long as their code can be
// DSO-loaded.
if (firstTime) {
firstTime = FALSE;
if (fieldName == "fields") {
if (!fieldData->readFieldDescriptions(in, this, 1<<20))
return TRUE;
continue;
}
}
SbBool foundName;
if (! fieldData->read(in, this, fieldName, foundName))
return FALSE;
// No match with any valid field name
// Assume it's gonna be an embedded part, of type SoSFNode
if (!foundName) {
// Add a node pointer field with this name to
// the unknownFieldData.
unknownFieldData->addField(this,fieldName.getString(),
new SoSFNode);
// Tell the unknownFieldData to read in this field...
if ( !unknownFieldData->read(in,this, fieldName, foundName))
return FALSE;
}
}
}
#ifdef DEBUG
SoNode *
SoBaseKit::typeCheck( const SbName &partName, const SoType &partType,
SoNode *node )
#else /* DEBUG */
SoNode *
SoBaseKit::typeCheck( const SbName &, const SoType &partType,
SoNode *node )
#endif /* DEBUG */
{
if ( node != NULL && !node->isOfType( partType ) ) {
#ifdef DEBUG
const char *typeName = partType.getName().getString();
SoDebugError::post("SoBaseKit::typeCheck",
"ERROR getting part! The part you asked for, "
"\"%s\", is not of the type you asked for, %s",
partName.getString(), typeName);
#endif
return NULL;
}
else
return node;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Redefines this to add this node and all part nodes to the dictionary.
//
// Use: protected, virtual
SoNode *
SoBaseKit::addToCopyDict() const
//
////////////////////////////////////////////////////////////////////////
{
// If this node is already in the dictionary, nothing else to do
SoNode *copy = (SoNode *) checkCopy(this);
if (copy == NULL) {
// Create and add a new instance to the dictionary
copy = (SoNode *) getTypeId().createInstance();
copy->ref();
addCopy(this, copy); // Adds a ref()
copy->unrefNoDelete();
// Recurse on all non-NULL parts. Skip part 0, which is "this".
for (int i = 1; i < nodekitPartsList->numEntries; i++) {
SoNode *partNode = nodekitPartsList->fieldList[i]->getValue();
if (partNode != NULL)
partNode->addToCopyDict();
}
}
return copy;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Copies the contents of the given nodekit into this instance.
//
// Use: protected, virtual
void
SoBaseKit::copyContents(const SoFieldContainer *fromFC, SbBool copyConnections)
//
////////////////////////////////////////////////////////////////////////
{
SbBool wasSetUp = setUpConnections( FALSE );
// Copy the standard node stuff (fields and names)
SoNode::copyContents(fromFC, copyConnections);
const SoBaseKit *origKit = (const SoBaseKit *) fromFC;
// We've got to do extra work now, for two reasons.
// First, we've got to make copies of all the parts. This is because the
// parts are stored in SoSFNode fields, which only copy the pointer
// and then call ref(). We need brand new copies.
// Second, nodekits have hidden children. Some may have been
// created during the constructor. But these pointers will differ
// from the part pointers in the field, so we want them deleted.
// The original also has hidden children that were created
// by setPart(), which we are missing. So we'll need to remove the
// hidden children, then insert them by calling setPart() repeatedly
// on all the parts.
//
// So, we need to:
// [1] create a temporary list to store copies of all part fields.
// [2] copy each part and store it in the temporary list.
// [2a] If NULL, just use NULL.
// [2b] else if parent part is 'this' use the copy of oldPart
// [2c] else we made this part already when we copied the
// ancestor part whose parent was 'this.' Find the parent.
// Then figure out which child to use for the part and
// set the part to be that node.
// [2d] Copy the default flag.
// [3] clear out the child list (remove all hidden children)
// [4] clear out the real part fields (remember, we've made copies!)
// [5] call setPart() for every non-NULL part in the saved part list.
// After this, our hidden children and part fields will be consistent.
// [6] delete the temporary copy of the part fields.
// [7] undo temporary ref of the copy.
// [1] create a temporary list to store copies of all part fields.
int numParts = origKit->nodekitPartsList->numEntries;
// firstPartField is 1. Part 0 is 'this' and is not treated here.
int firstPartField = 1;
SoSFNode **realPartFldLst = nodekitPartsList->fieldList;
SoSFNode **tempPartFldLst = new SoSFNode *[numParts];
int i;
for ( i = firstPartField; i < numParts; i++ )
tempPartFldLst[i] = new SoSFNode;
// [2] copy each part and store it in the temporary list.
// [2a] If NULL, just use NULL.
// [2b] else if parent part is 'this' use the copy of oldPart
// [2c] else we made this part already when we copied the
// ancestor part whose parent was 'this.' Find the parent.
// Then figure out which child to use for the part and
// set the part to be that node.
// [2d] Copy the default flag.
const SoNodekitCatalog *cat = origKit->getNodekitCatalog();
SoSFNode **origPartFldLst = origKit->nodekitPartsList->fieldList;
SoNode *copyPart, *origPart;
SoNode *copyParentPartNode, *origParentPartNode;
SoGroup *copyParentPartGroup, *origParentPartGroup;
int parentPNum;
SoType grpType = SoGroup::getClassTypeId();
for ( i = firstPartField; i < numParts; i++ ) {
if (realPartFldLst[i]->getValue() == NULL ) {
// [2a] If NULL, just use NULL.
copyPart = NULL;
}
else if ((parentPNum = cat->getParentPartNumber(i))
== SO_CATALOG_THIS_PART_NUM) {
// [2b] else if parent part is 'this' use the copy of
// oldPart. The copy has already been created, but we
// need to fill in its contents.
copyPart = realPartFldLst[i]->getValue();
copyPart->copyContents(origPartFldLst[i]->getValue(),
copyConnections);
}
else {
// [2c] else we made this part already when we copied the
// ancestor part whose parent was 'this.' Find the parent.
// Then figure out which child to use for the part and
// set the part to be that node.
copyParentPartNode = tempPartFldLst[ parentPNum ]->getValue();
origParentPartNode = origPartFldLst[ parentPNum ]->getValue();
#ifdef DEBUG
if ( !copyParentPartNode || !origParentPartNode )
SoDebugError::post("SoBaseKit::copyContents",
"copy or original of parent part is NULL");
if ( !copyParentPartNode->isOfType(grpType) ||
!origParentPartNode->isOfType(grpType) )
SoDebugError::post("SoBaseKit::copyContents",
"copy or original of parent part not an SoGroup");
#endif
copyParentPartGroup = (SoGroup *) copyParentPartNode;
origParentPartGroup = (SoGroup *) origParentPartNode;
origPart = origPartFldLst[i]->getValue();
int kidIndex = origParentPartGroup->findChild( origPart );
#ifdef DEBUG
if ( kidIndex < 0 )
SoDebugError::post( "SoBaseKit::copyContents",
"original part not found under original part parent");
if ( copyParentPartGroup->getNumChildren() < (kidIndex + 1) )
SoDebugError::post( "SoBaseKit::copyContents",
"copy of parent part does not have enough children");
#endif
copyPart = copyParentPartGroup->getChild(kidIndex);
}
tempPartFldLst[i]->setValue( copyPart );
// [2d] Copy the default flag.
tempPartFldLst[i]->setDefault(realPartFldLst[i]->isDefault() );
}
// [3] clear out the child list (remove all hidden children)
for ( i = getNumChildren() - 1; i >= 0; i-- )
removeChild( i );
// [4] clear out the real part fields (remember, we've made copies!)
for ( i = firstPartField; i < numParts; i++ ) {
realPartFldLst[i]->setValue( NULL );
realPartFldLst[i]->setDefault( TRUE );
}
// [5] Call setPart() for every non-NULL part in the saved part list.
// After this, our hidden children and part fields will be consistent.
// [5a] Skip part 0, because part 0 is "this"
// [5c] For non-leaves, remove all children.
// They will be added back when (and if) setPart() is
// called on the child.
// [5d] Set the part, using setPartFromThisCatalog()
// [5e] Copy EVERY default flag from tempPartFldLst to
// realPartFldLst. Sometimes, a NULL part has
// (default == FALSE).
// [5a] Skip part 0, because part 0 is "this"
for ( i = firstPartField; i < numParts; i++ ) {
if ( tempPartFldLst[i]->getValue() != NULL ) {
SoNode *n = tempPartFldLst[i]->getValue();
n->ref();
// [5c] For non-leaves, remove all children.
// They will be added back when (and if) setPart() is
// called on the child.
if (cat->isLeaf(i) == FALSE ) {
SoGroup *g = (SoGroup *) n;
for (int j = g->getNumChildren()-1; j>=0; j-- )
g->removeChild(j);
}
// [5d] Set the part, using setPartFromThisCatalog()
nodekitPartsList->setPartFromThisCatalog( i, n, TRUE );
n->unref();
}
// [5e] Copy EVERY default flag from tempPartFldLst to
// realPartFldLst. Sometimes, a NULL part has
// (default == FALSE).
realPartFldLst[i]->setDefault( tempPartFldLst[i]->isDefault() );
}
// [6] delete the temporary copy of the part fields.
for ( i = firstPartField; i < numParts; i++ ) {
delete tempPartFldLst[i];
}
delete [] tempPartFldLst;
// [7] re-instate connections of the copy.
setUpConnections( wasSetUp );
}
SoChildList *
SoBaseKit::getChildren() const
{
return children;
}
void
SoBaseKit::doAction( SoAction *action )
{
int numIndices;
const int *indices;
if (action->getPathCode(numIndices, indices) == SoAction::IN_PATH)
children->traverse(action, 0, indices[numIndices - 1]);
else
children->traverse(action);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Implements get matrix action.
//
// Use: protected
void
SoBaseKit::getMatrix(SoGetMatrixAction *action)
//
////////////////////////////////////////////////////////////////////////
{
int numIndices;
const int *indices;
// Only need to compute matrix if group is a node in middle of
// current path chain or is off path chain (since the only way
// this could be called if it is off the chain is if the group is
// under a group that affects the chain).
switch (action->getPathCode(numIndices, indices)) {
case SoAction::NO_PATH:
break;
case SoAction::IN_PATH:
children->traverse(action, 0, indices[numIndices - 1]);
break;
case SoAction::BELOW_PATH:
break;
case SoAction::OFF_PATH:
children->traverse(action);
break;
}
}
// These functions implement all actions for nodekits.
void
SoBaseKit::callback( SoCallbackAction *action )
{
SoBaseKit::doAction( action );
}
void
SoBaseKit::GLRender( SoGLRenderAction *action )
{ SoBaseKit::doAction( action ); }
void
SoBaseKit::getBoundingBox( SoGetBoundingBoxAction *action )
{
SbVec3f totalCenter(0,0,0);
int numCenters = 0;
int numIndices;
const int *indices;
int lastChild;
if (action->getPathCode(numIndices, indices) == SoAction::IN_PATH)
lastChild = indices[numIndices - 1];
else
lastChild = getNumChildren() - 1;
for (int i = 0; i <= lastChild; i++) {
children->traverse(action, i, i);
if (action->isCenterSet()) {
totalCenter += action->getCenter();
numCenters++;
action->resetCenter();
}
}
// Now, set the center to be the average:
if (numCenters != 0)
action->setCenter(totalCenter / numCenters, FALSE);
}
void
SoBaseKit::handleEvent( SoHandleEventAction *action )
{ SoBaseKit::doAction( action ); }
void
SoBaseKit::rayPick( SoRayPickAction *action )
{
SoBaseKit::doAction( action );
// Look at the picked point. If no detail has been constructed yet for
// this node in the pickedPoint's path list, then do so. (In the case
// of instancing, the detail might already exist.)
const SoPickedPointList &pointList
= (const SoPickedPointList &) action->getPickedPointList();
SoPickedPoint *kidPoint;
SoFullPath *pointPath;
for (int i = 0; i < pointList.getLength(); i++ ) {
kidPoint = pointList[i];
pointPath = (SoFullPath *) kidPoint->getPath();
// See if there is a detail for this node.
if ( pointPath->containsNode( this )
&& kidPoint->getDetail(this) == NULL ) {
// Set up the nodekit detail.
SoNodeKitDetail *newDetail = new SoNodeKitDetail;
newDetail->setNodeKit( this );
// Walk down the path until you find a node that is a leaf
// in this kit's catalog. That will be the 'part' in the
// detail
// First, find index of 'this' in path
int startIndex;
for ( int j = 0; j < pointPath->getLength(); j++ ) {
startIndex = j;
if ( pointPath->getNode(j) == this )
break;
}
// Next, walk down path starting at startIndex
const SoNodekitCatalog *cat = getNodekitCatalog();
SoNode *thePart;
int thePartNum, partPathIndex;
for ( int k = startIndex; k < pointPath->getLength(); k++ ) {
thePart = pointPath->getNode(k);
partPathIndex = k;
// Find the part number of this part in the catalog
for (int l = 0; l < nodekitPartsList->numEntries; l++ ){
thePartNum = l;
if (l==0) {
if (nodekitPartsList->rootPointer == thePart)
break;
}
else {
if ( nodekitPartsList->fieldList[l]->getValue()
== thePart )
break;
}
}
if (cat->isLeaf(thePartNum) == TRUE )
break;
}
// If the part was a nodeKitListPart, then see if one
// of it's children is also in the path. If so, we'll
// give back a part name like "childList[0]" instead of
// just childList.
int childNumber = -1;
if (thePart->isOfType(SoNodeKitListPart::getClassTypeId())){
if ( pointPath->getLength() >= (partPathIndex + 2) ) {
// The path contains the grandchild of the list
// part. (The child is the container, the
// grandchild is the actual 'child' that we expose)
childNumber = pointPath->getIndex(partPathIndex+2);
}
}
newDetail->setPart( thePart );
SbName thePartName;
if ( childNumber == -1 )
thePartName = cat->getName(thePartNum);
else {
char fullStr[100];
sprintf(fullStr, "%s[%d]",
cat->getName(thePartNum).getString(), childNumber );
thePartName = fullStr;
}
newDetail->setPartName( thePartName );
kidPoint->setDetail( newDetail, this );
}
}
}
void
SoBaseKit::search( SoSearchAction *action )
{
SoNode::search(action);
if (isSearchingChildren())
SoBaseKit::doAction( action );
}
void
SoBaseKit::write( SoWriteAction *action )
{
SoOutput *out = action->getOutput();
if (out->getStage() == SoOutput::COUNT_REFS) {
// Increment the write reference count. We have our own version
// that treats fields for our parts differently.
// If a part field is set to default, we try not to write it.
// So we give it a field-style reference. If, in addition, the
// part is of type SoBaseKit, we'll count the fields within that
// part as well. This insures that if a part nested beneath our part
// must write, then our part will write as well.
addWriteReference(out);
// If this environment variable is set, we are going to write
// out the children the same way that SoGroup does. This is ONLY for
// debugging purposes.
#ifdef DEBUG
if (!out->isBinary() &&
SoDebug::GetEnv("IV_DEBUG_WRITE_KIT_CHILDREN")) {
if ( !hasMultipleWriteRefs())
SoBaseKit::doAction(action);
}
#endif
}
else if (! writeHeader(out, FALSE, FALSE)) {
// We are in the WRITE stage. We can assume that if we got here,
// this instance has at least one write reference.
// Write out fields (including part fields!) using
// 'fieldDataForWriting. Then delete 'fieldDataForWriting'.
if (fieldDataForWriting != NULL) {
const SoNodekitCatalog *cat = getNodekitCatalog();
SoSFNode **pFields = nodekitPartsList->fieldList;
int numParts = nodekitPartsList->numEntries;
// Determine if we must force any default parts to write and
// call setDefault(FALSE) on them. This way, they'll write.
// The simple test is the regular shouldWrite(). If this
// fails on part of type nodekit, we must also check all of its
// nested parts before being certain.
SoNode *partNode;
for ( int i = 1; i < numParts; i++ ) {
if ( ! pFields[i]->isDefault() )
continue;
partNode = pFields[i]->getValue();
if ( !partNode )
continue;
if ( partNode->shouldWrite()) {
pFields[i]->setDefault(FALSE);
continue;
}
if ( partNode->isOfType( SoBaseKit::getClassTypeId()) &&
((SoBaseKit *)partNode)->forceChildDrivenWriteRefs(out)) {
// If the kit has changed its mind and upped its
// write ref count because a descended child must write,
// then:
pFields[i]->setDefault(FALSE);
continue;
}
}
// Now, do the usual writing bit.
fieldDataForWriting->write(out, this);
// Get rid of fieldDataForWriting.
delete fieldDataForWriting;
fieldDataForWriting = NULL;
}
#ifdef DEBUG
if ( !out->isBinary() &&
SoDebug::GetEnv("IV_DEBUG_WRITE_KIT_CHILDREN")) {
// If this env variable is set, write children like an SoGroup.
// This is ONLY for debugging purposes, to check for consistency
// of childList and part fields.
fprintf(stderr,"NODEKIT DEBUG: Writing Children of this NodeKit\n");
fprintf(stderr," to file. Do not try to read in the result\n");
// In the writing stage, we can't use standard traversal,
// because if we are writing out one path of a path list, we
// might need to write out children that are not relevant to
// the current path (but are relevant to another path in the
// list). Therefore, we implement a different traversal that
// writes out each child that is referenced.
for (int i = 0; i <= getNumChildren(); i++)
if (getChild(i)->shouldWrite())
children->traverse(action, i);
}
#endif
writeFooter(out);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Overrides this method to use countMyFields instead of
// the regular SoFieldData writing mechanism.
//
// Use: internal, virtual
void
SoBaseKit::addWriteReference(SoOutput *out, SbBool isFromField)
//
////////////////////////////////////////////////////////////////////////
{
// Do standard stuff
SoBase::addWriteReference(out, isFromField);
// If this is the first reference to this instance and we are not
// being referenced from a field-to-field connection, give fields
// a chance to do their thing
// We can not use the regular fieldData write method, because
// we do special stuff with our part fields.
if (! isFromField && ! hasMultipleWriteRefs() ) {
countMyFields( out );
}
}
// This returns TRUE if the nodekit intends to write out.
// [a] call shouldWrite(). If TRUE, trivial return.
// [b] If the kit thinks it shouldn't write, it first does a recursive
// call to its children. If any children must write, then so must
// the kit.
// [c] If kit has changed its mind because of [b], then add a writeRef.
//
// [d] If kit should not write, it will delete the fieldDataForWriting,
// since there will no writing pass applied to take care of this.
SbBool
SoBaseKit::forceChildDrivenWriteRefs( SoOutput *out )
{
// [a] call shouldWrite(). If TRUE, trivial return.
if ( shouldWrite() )
return TRUE;
// If NULL, we've already failed this test once (and therefore deleted it),
// or never even attempted to count references.
if ( fieldDataForWriting == NULL )
return FALSE;
// [b] If the kit thinks it shouldn't write, it first does a recursive
// call to its children. If any children must write, then so must
// the kit.
SbBool writeMe = FALSE;
const SoNodekitCatalog *cat = getNodekitCatalog();
int partNumber;
SoField *field;
SoSFNode *partField;
SoNode *partNode;
for (int i = 0; i < fieldDataForWriting->getNumFields(); i++) {
field = fieldDataForWriting->getField(this, i);
// If field is not default, we write...
if ( ! field->isDefault() ) {
writeMe = TRUE;
break;
}
partNumber = cat->getPartNumber(fieldDataForWriting->getFieldName(i));
if ( partNumber == SO_CATALOG_NAME_NOT_FOUND ) {
// non-parts that are ignored cause us to write.
if ( field->isIgnored()) {
writeMe = TRUE;
break;
}
}
else {
// The field is a part field set to default.
partField = (SoSFNode *) field;
// If part is NULL, move onto the next field.
if (partField->getValue() == NULL)
continue;
// Else, get the partNode...
partNode = partField->getValue();
// If partNode should write, so should we...
if ( partNode->shouldWrite() ) {
partField->setDefault(FALSE);
writeMe = TRUE;
break;
}
// If partNode is a nodekit, recurse.
if ( partNode->isOfType(SoBaseKit::getClassTypeId()) ) {
if (((SoBaseKit *)partNode)->forceChildDrivenWriteRefs(out) == TRUE )
partField->setDefault(FALSE);
writeMe = TRUE;
break;
}
}
}
// [c] If kit has changed its mind because of [b], then add a writeRef.
// Then return.
if (writeMe) {
SoBase::addWriteReference(out);
return TRUE;
}
// [d] If kit should not write, it will delete the fieldDataForWriting,
// since there will no writing pass applied to take care of this.
delete fieldDataForWriting;
fieldDataForWriting = NULL;
return FALSE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
//
// This is called during addWriteReference() on parts of the nodekit that
// are in turn nodekits. Under normal circumstances, it is not used.
// It is only called if the kit-part has a non-NULL value, but has been
// set to default. (This indicates that we would rather not write it).
// Even though it is default, it will be forced to write if any of its
// fields or ancestor parts are required to write.
// This method adds a field-connection style write reference to the
// kit-part itself, then allows the kit-part to add write references to
// its fields and part-fields.
// Later, in the WRITE stage, the kit-part will be written if at least
// one of its fields or ancestor parts has shouldWrite() ==TRUE.
// Otherwise, it won't.
// Example:
// Parent draggers attempt not to write out child draggers.
// But the parentDragger must at least traverse the childDragger to see
// if any of the part geometry has been changed from its default. Such
// changes must be written to file.
//
// Use: protected.
//
////////////////////////////////////////////////////////////////////////
void
SoBaseKit::countMyFields(SoOutput *out )
{
if (out->getStage() == SoOutput::COUNT_REFS) {
// In first pass, 'fieldDataForWriting' should always
// be NULL, since we delete it at the end of the WRITE stage.
if (fieldDataForWriting != NULL) {
// We've already been here once during this write action.
// Just return.
return;
}
// Make version of fieldData that reorders fields so they write prettier
createFieldDataForWriting();
// This virtual function will call setDefault(TRUE) on those
// fields which we do not wish to write out.
setDefaultOnNonWritingFields();
// This (non-virtual) function will undo the setDefault on
// those parts which simply MUST write out, even though
// they have been set to default. This prevents errors in subclassing.
undoSetDefaultOnFieldsThatMustWrite();
// Now, count the write refs caused by our fields.
const SoNodekitCatalog *cat = getNodekitCatalog();
int partNumber;
SoField *field;
SoSFNode *partField;
SoNode *partNode;
for (int i = 0; i < fieldDataForWriting->getNumFields(); i++) {
const SbName fieldName = fieldDataForWriting->getFieldName(i);
field = fieldDataForWriting->getField(this, i);
partNumber = cat->getPartNumber( fieldName );
if ( partNumber == SO_CATALOG_NAME_NOT_FOUND ) {
// If the field is not a part, do things the same way
// that SoFieldData does:
if ( ! field->isDefault() || field->isIgnored())
field->write( out, fieldName );
}
else {
// The field is for a part.
if ( !field->isDefault() ) {
// If it's not default, just write it out...
field->write(out, fieldName );
}
else {
// The field is a part field set to default.
// First, cast field to an SoSFNode:
partField = (SoSFNode *) field;
// The regular SoSFNode::countWriteRefs() looks like this:
// We call SoField::countWriteRefs() to count any
// fields/engines we are connected from.
// But we don't want to write an instance of the part.
// We're hoping it will not have to write.
// SoField::countWriteRefs(out);
// if (value != NULL)
// value->writeInstance(out);
// Just like we said we'd do...
partField->SoField::countWriteRefs(out);
// If the part is NULL, we're done with this field.
if (partField->getValue() == NULL)
continue;
// Else, get the partNode...
partNode = partField->getValue();
// Put a field-connection style write reference on partNode.
// This only makes partNode write if another instance is
// forcing a write anyway.
// Since it is a field-connection, the method will not
// traverse the fields of the node.
partNode->addWriteReference(out, TRUE);
if ( partNode->isOfType(SoBaseKit::getClassTypeId()) ) {
// If partNode is a nodekit, write the fields as well.
((SoBaseKit *)partNode)->countMyFields(out );
}
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////
//
// Copy the fields in fieldData into a new structure.
// We make the following changes to order in which fields are written:
// [1] Write out fields that are not named in the catalog first.
// [2] Write out leaf nodes from the catalog.
// [3] Write out non-leaf nodes from the catalog.
//
/////////////////////////////////////////////////////////////////////////
void
SoBaseKit::createFieldDataForWriting()
{
const SoNodekitCatalog *cat = getNodekitCatalog();
const SoFieldData *fd = getFieldData();
fieldDataForWriting = new SoFieldData;
int i, pNum;
// [1] Write out fields that are not named in the catalog first.
for ( i = 0; i < fd->getNumFields(); i++ ) {
pNum = cat->getPartNumber( fd->getFieldName(i) );
if ( pNum == SO_CATALOG_NAME_NOT_FOUND )
fieldDataForWriting->addField(this,
fd->getFieldName(i).getString(), fd->getField(this,i) );
}
// [2] Write out leaf nodes before non-leaf nodes.
for ( i = 0; i < fd->getNumFields(); i++ ) {
pNum = cat->getPartNumber( fd->getFieldName(i) );
if ( pNum != SO_CATALOG_NAME_NOT_FOUND && cat->isLeaf(pNum) == TRUE )
fieldDataForWriting->addField(this,
fd->getFieldName(i).getString(), fd->getField(this,i) );
}
// [3] Write out non-leaf nodes from the catalog.
for ( i = 0; i < fd->getNumFields(); i++ ) {
pNum = cat->getPartNumber( fd->getFieldName(i) );
if ( pNum != SO_CATALOG_NAME_NOT_FOUND && cat->isLeaf(pNum) == FALSE )
fieldDataForWriting->addField(this,
fd->getFieldName(i).getString(), fd->getField(this,i) );
}
}
/////////////////////////////////////////////////////////////////////////
//
// call setDefault(TRUE) on the part fields (of type SoSFNode) for
// those parts which we do not wish to write out.
//
// We use the following test:
//
// First of all, we skip all the work if part already has isDefault() == TRUE
//
// Don't have to write out if:
// [NULL Test]: value is NULL and nullByDefault is TRUE
// Otherwise, it must either be:
// [Leaf SoGroup with no children]
// Or:
// [Leaf SoSeparator with no children]
// Or:
// [Leaf ListPart with no children and a container that's a group or sep]
// Or all three of the following:
// [NonLeaf Test]: part is not a leaf part AND
// [Group Test]: node is non-NULL of type SoGroup AND
// [FieldValue Test]: (node->isNodeFieldValuesImportant() == FALSE)
//
/////////////////////////////////////////////////////////////////////////
void
SoBaseKit::setDefaultOnNonWritingFields()
{
const SoNodekitCatalog *cat = getNodekitCatalog();
SoSFNode **pFields = nodekitPartsList->fieldList;
int numP = nodekitPartsList->numEntries;
SoNode *node;
for ( int partNum = 1; partNum < numP; partNum++ ) {
// Skip all the work if part already has isDefault() == TRUE
if ( pFields[partNum]->isDefault() )
continue;
// [NULL Test]: value is NULL and nullByDefault is TRUE
node = pFields[partNum]->getValue();
if ( node == NULL && cat->isNullByDefault(partNum) ) {
pFields[partNum]->setDefault(TRUE);
continue;
}
SoType grpType = SoGroup::getClassTypeId();
SoType sepType = SoSeparator::getClassTypeId();
if ( cat->isLeaf(partNum) && node != NULL ) {
// [Leaf SoGroup with no children]
if ( node->getTypeId() == grpType
&& ((SoGroup *)node)->getNumChildren() == 0) {
pFields[partNum]->setDefault(TRUE);
continue;
}
// Or:
// [Leaf SoSeparator with no children]
if ( node->getTypeId() == sepType
&& ((SoGroup *)node)->getNumChildren() == 0) {
pFields[partNum]->setDefault(TRUE);
continue;
}
// Or:
// [Leaf ListPart with no kids and a group or sep container]
if ( node->getTypeId() == SoNodeKitListPart::getClassTypeId()
&& ((SoNodeKitListPart *)node)->getNumChildren() == 0 ) {
SoGroup *cn = ((SoNodeKitListPart *)node)->getContainerNode();
if (cn->getTypeId() == sepType || cn->getTypeId() == grpType) {
pFields[partNum]->setDefault(TRUE);
continue;
}
}
}
// Otherwise, must pass all three of the following:
// [NonLeaf Test]: part is not a leaf part AND
if (cat->isLeaf(partNum) == TRUE )
continue;
// [Group Test]: node is non-NULL of type SoGroup AND
if ( node == NULL ||
( ! node->isOfType(SoGroup::getClassTypeId())))
continue;
// [FieldValue Test]: (node->isNodeFieldValuesImportant() == FALSE)
if ( isNodeFieldValuesImportant( node ))
continue;
// passed all three tests...
pFields[partNum]->setDefault(TRUE);
}
}
// Called by write() after the (virtual) setDefaultOnNonWritingNodes()
// method. This method looks at the part fields which have isDefault()
// set to TRUE. It calls setDefault(FALSE) on any part fields
// that MUST write.
// This happens when the part-field is for a part whose parent
// is going to write out anyway. Therefore, it will appear in file as
// a node within this kit, so we better write out the part field to
// explain where the node belongs in the kit.
void
SoBaseKit::undoSetDefaultOnFieldsThatMustWrite()
{
const SoNodekitCatalog *cat = getNodekitCatalog();
SoSFNode **pFields = nodekitPartsList->fieldList;
int numP = nodekitPartsList->numEntries;
SoNode *node;
for ( int partNum = 1; partNum < numP; partNum++ ) {
// Skip if already not default.
if ( ! pFields[partNum]->isDefault() )
continue;
// NULL can always be written as default.
node = pFields[partNum]->getValue();
if ( node == NULL )
continue;
// Parent must either be 'this' or it must also be
// set to default. Otherwise this part will get written
// out as a child of the parent anyway.
int prntPartNum = cat->getParentPartNumber( partNum );
if (prntPartNum != SO_CATALOG_THIS_PART_NUM &&
pFields[prntPartNum]->isDefault() == FALSE ) {
pFields[partNum]->setDefault(FALSE);
continue;
}
}
}
SbBool
SoBaseKit::isNodeFieldValuesImportant( SoNode *node )
{
// Create an instance of the same type - it will have default values
// Use this for comparing in [Test1 ]
SoFieldContainer *def
= (SoFieldContainer *) node->getTypeId().createInstance();
def->ref();
const SoFieldData *fd = node->getFieldData();
for ( int i = 0; i < fd->getNumFields(); i++ ) {
// A field is important if:
// [Test 1]: field has (isDefault() != TRUE) &&
// [Test 2]: field does not have default value
if ( ! fd->getField(node, i)->isDefault() &&
(! fd->getField(node, i)->isSame(*fd->getField(def, i)))) {
def->unref();
return TRUE;
}
}
// Get rid of default instance
def->unref();
return FALSE;
}
void
SoBaseKit::replaceChild( int index, SoNode *newChild )
{
// copied from SoGroup...
#ifdef DEBUG
if (index < 0 || index >= getNumChildren()) {
SoDebugError::post( "SoBaseKit::replaceChild",
"Index %d is out of range %d - %d", index, 0, getNumChildren() - 1);
return;
}
#endif /* DEBUG */
// Play it safe anyway...
if (index >= 0)
children->set(index, newChild);
}
void
SoBaseKit::removeChild( int index )
{
// copied from SoGroup
#ifdef DEBUG
if (index < 0 || index >= getNumChildren()) {
SoDebugError::post( "SoBaseKit::removeChild",
"Index %d is out of range %d - %d", index, 0, getNumChildren() - 1);
return;
}
#endif /* DEBUG */
// Play it safe anyway...
if (index >= 0) {
children->remove(index);
}
}
void
SoBaseKit::addChild( SoNode *child )
{
#ifdef DEBUG
if (child == NULL) {
SoDebugError::post( "SoBaseKit::addChild", "NULL child node");
return;
}
#endif /* DEBUG */
children->append(child);
}
int
SoBaseKit::findChild( const SoNode *child) const
{
int i, num;
num = getNumChildren();
for (i = 0; i < num; i++)
if (getChild(i) == child) return(i);
return(-1);
}
void
SoBaseKit::insertChild(SoNode *child, int newChildIndex)
{
#ifdef DEBUG
if (child == NULL) {
SoDebugError::post( "SoBaseKit::insertChild", "NULL child node");
return;
}
// Make sure index is reasonable
if (newChildIndex < 0 || newChildIndex > getNumChildren()) {
SoDebugError::post( "SoBaseKit::insertChild",
"Index %d is out of range %d - %d",newChildIndex, 0, getNumChildren());
return;
}
#endif /* DEBUG */
// See if adding at end
if (newChildIndex >= getNumChildren())
children->append(child);
else
children->insert(child, newChildIndex);
}
void
SoBaseKit::setSearchingChildren( SbBool newVal )
{
searchingChildren = newVal;
}