/**************************************************************************\
*
* 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 SoBaseKit SoBaseKit.h Inventor/nodekits/SoBaseKit.h
\brief The SoBaseKit class is the toplevel superclass for nodekits.
\ingroup nodekits
Node kits are collections of nodes and other node kits (from here on
node kits which is part of some other node kit, will only be referred
to as nodes or parts, see catalogs and parts), organized in a way
that is convenient for its use. A node kit inherits SoNode and can
thus be inserted into a scenegraph as any other node.
The organizing of the nodes and node kits of some node kit, is done
through catalogs. A node kit's catalog describes the nodes that can
be members of the node kit. These members are called parts. Thus a
node kit has a catalog describing the parts that it offers to the
user.
Each part in the catalog has some values saying something about the
part itself and about the role the part plays in the scenegraph.
Those values are:
- Name
- The name of the part.
- Type
- The part's node type.
- Default Type
- If the part's type is an abstract superclass, this value will hold
the default subclass used by this part.
- Created by Default?
- Holds \c TRUE if the part should be instantiated when the node kit
is instantiated, otherwise the part is kept empty until it is set by some
of the means applicable.
- Parent Name
- The name of the part that is this part's parent.
- Right Sibling
- The name of the part that is the part immediately to the right of
this part in the node kit scenegraph.
- Is it a List?
- Holds \c TRUE if the part is a list, otherwise it is \c FALSE. See
SoNodeKitListPart for more info on node kit lists.
- List Cointainer Type
- The type of group node used to hold the items if the part is a list.
- List Element Type
- The types of nodes that is allowed to be held by this part if the part
is a list.
- Is It Public?
- Holds \c TRUE if the part should be publically available, otherwise
it holds \c FALSE.
Node kits use lazy instantiation when it creates it's parts. This means
that the nodes making up the parts of the nodekit only are created when
they are needed. If the "Created by Default?" holds TRUE, then the part
is created when the node kit itself is instantiated. If not, parts are
created when they are requested through SoBaseKit::getPart() or the
SO_GET_PART() macro, or created with SoBaseKit::set(). Also, if a part is
set with SoBaseKit::setPart() or the SO_SET_PART() macro, any previously
uncreated parts above the set part in the hierarchy, is created
automatically.
The advantages of using node kits to represent a scenegraph are many.
\li Since a node kit collects nodes into a single unit, it becomes
an extra abstraction layer for the application programmer. Such
a layer can represent a model of a human being as one unit where
subunits as arms, legs, textures, etc are contained within. Thus
we can instantiate a model of a human by creating an instance of
the node kit, instead of having to create a possibly large
amount of nodes needed for such a model.
\li A part of the node kit doesn't have one spesific setup. A shape part
can e.g. be swapped with any other shape, since they are of the same
type. If the node kit of a human has a part called "head" which is of
type SoShape, it might default to a sphere. But if the programmer
thinks that a cube might fit better, one can set the "head" part to
a cube instead, or maybe a face set representing a complex model of
a head.
\li Node kits can have as simple or as complex catalogs as needed. The
nodes included in the node kit can, if needed, represent the
whole range of Inventor features. One part can as easily be of a
node kit type, making it possible to create hierarchys of node kits.
Having a node kit of a human, it might be feasible to have sub node
kits describing the different body parts.
\li Node kits are an efficient way of creating scenegraphs. If some
part of it isn't needed at the moment of node kit instantiation,
they aren't created. Thus parts are only created when needed, either
by the application or some other part.
\li The application code becomes smaller and easier to read, as the node
kits provides simple routines for creating and setting parts.
\li New node kits can be created through subclassing to obtain simple
setups of scenegraphs best fitted to the application.
The usage of a node kit is straightforward. Below follows a code
example showing some simple SoShapeKit usage.
\code
#include
#include
#include
#include
#include
int
main(int argc, char ** argv)
{
QWidget * window = SoQt::init(argv[0]);
SoQtExaminerViewer * viewer = new SoQtExaminerViewer(window);
// Instantiating a shape kit, by default creating a simple sphere.
SoShapeKit * shapekit = new SoShapeKit;
// Swapping the sphere with a cube.
shapekit->setPart("shape", new SoCube);
// Setting the cube to be rendered in the color red. The shape kit
// has a SoAppearanceKit as one of it's parts. The "material" part
// used to set the color of the shape, really belongs the
// appearance kit. If the SoShapeKit::set() is used, it will
// check if some of its sub kits has a part with the name given,
// and delegate the setting to the correct kit.
shapekit->set("material", "diffuseColor 1 0 0");
SoSeparator * newroot = new SoSeparator;
newroot->ref();
newroot->addChild(shapekit);
viewer->setSceneGraph(newroot);
viewer->show();
SoQt::show(window);
SoQt::mainLoop();
delete viewer;
newroot->unref();
return 0;
}
\endcode
The above code snippet will produce a viewer with a side view to
the scene shown below:
Notice that the code needed for creating this simple shape using
a shape kit, amounts to this:
\code
SoShapeKit * shapekit = new SoShapeKit;
shapekit->setPart("shape", new SoCube);
shapekit->set("material", "diffuseColor 1 0 0");
\endcode
..while doing it without shape kits amounts to this:
\code
SoSeparator * root = new SoSeparator;
SoMaterial * material = new SoMaterial;
material->diffuseColor.setValue(1,0,0);
root->addChild(material);
root->addChild(new SoCube);
\endcode
..so even for this miniscule mock-up example, you save on code
verbosity and complexity.
\TOOLMAKER_REF
Following is a complete example of a node kit extension. The node
kit is a kit which automatically scales a shape so it will be the
same size in screen-pixels, no matter which distance it is from the
camera. This is useful for marker graphics. The shape defaults to a
cube, but can be set by the programmer to any shape or scene
sub-graph.
The header file:
\code
// Copyright (C) 1998-2005 by Systems in Motion. All rights reserved.
#ifndef COIN_SHAPESCALE_H
#define COIN_SHAPESCALE_H
#include
#include
#include
class SbViewport;
class SoState;
class SbColor;
class SbVec2s;
class ShapeScale : public SoBaseKit {
typedef SoBaseKit inherited;
SO_KIT_HEADER(ShapeScale);
SO_KIT_CATALOG_ENTRY_HEADER(topSeparator);
SO_KIT_CATALOG_ENTRY_HEADER(scale);
SO_KIT_CATALOG_ENTRY_HEADER(shape);
public:
ShapeScale(void);
static void initClass(void);
SoSFFloat active;
SoSFFloat projectedSize;
protected:
virtual void GLRender(SoGLRenderAction * action);
virtual ~ShapeScale();
};
#endif // ! SHAPESCALE_H
\endcode
The source code for the example:
\code
// Copyright (C) 1998-2005 by Systems in Motion. All rights reserved.
// The ShapeScale class is used for scaling a shape based on
// projected size.
//
// This nodekit can be inserted in your scene graph to add for
// instance 3D markers that will be of a constant projected size.
//
// The marker shape is stored in the "shape" part. Any kind of node
// can be used, even group nodes with several shapes, but the
// marker shape should be approximately of unit size, and with a
// center position in (0, 0, 0).
// SoSFFloat ShapeScale::active
// Turns the scaling on/off. Default value is TRUE.
// SoSFFloat ShapeScale::projectedSize
// The requested projected size of the shape. Default value is 5.0.
#include "ShapeScale.h"
#include
#include
#include
#include
#include
#include
#include
#include
SO_KIT_SOURCE(ShapeScale);
// Constructor.
ShapeScale::ShapeScale(void)
{
SO_KIT_CONSTRUCTOR(ShapeScale);
SO_KIT_ADD_FIELD(active, (TRUE));
SO_KIT_ADD_FIELD(projectedSize, (5.0f));
SO_KIT_ADD_CATALOG_ENTRY(topSeparator, SoSeparator, FALSE, this, \x0, FALSE);
SO_KIT_ADD_CATALOG_ABSTRACT_ENTRY(shape, SoNode, SoCube, TRUE, topSeparator, \x0, TRUE);
SO_KIT_ADD_CATALOG_ENTRY(scale, SoScale, FALSE, topSeparator, shape, FALSE);
SO_KIT_INIT_INSTANCE();
}
// Destructor.
ShapeScale::~ShapeScale()
{
}
// Initializes this class. Call before using it.
void
ShapeScale::initClass(void)
{
SO_KIT_INIT_CLASS(ShapeScale, SoBaseKit, "BaseKit");
}
static void
update_scale(SoScale * scale, const SbVec3f & v)
{
// only write to field when scaling has changed.
if (scale->scaleFactor.getValue() != v) {
scale->scaleFactor = v;
}
}
// Overridden to (re)initialize scaling before rendering marker.
void
ShapeScale::GLRender(SoGLRenderAction * action)
{
SoState * state = action->getState();
SoScale * scale = (SoScale*) this->getAnyPart(SbName("scale"), TRUE);
if (!this->active.getValue()) {
update_scale(scale, SbVec3f(1.0f, 1.0f, 1.0f));
}
else {
const SbViewportRegion & vp = SoViewportRegionElement::get(state);
const SbViewVolume & vv = SoViewVolumeElement::get(state);
SbVec3f center(0.0f, 0.0f, 0.0f);
float nsize = this->projectedSize.getValue() / float(vp.getViewportSizePixels()[1]);
SoModelMatrixElement::get(state).multVecMatrix(center, center); // transform to WCS
float scalefactor = vv.getWorldToScreenScale(center, nsize);
update_scale(scale, SbVec3f(scalefactor, scalefactor, scalefactor));
}
inherited::GLRender(action);
}
\endcode
And a complete example showing how one can use this node kit:
\code
// Copyright (C) 1998-2005 by Systems in Motion. All rights reserved.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ShapeScale.h"
// Returns random value between 0.0f and 1.0f.
static float
normalized_rand(void)
{
return float(rand())/float(RAND_MAX);
}
static SoSeparator *
construct_new_marker(const SbVec3f & v)
{
SoSeparator * markerroot = new SoSeparator;
SoTranslation * t = new SoTranslation;
t->translation = v;
markerroot->addChild(t);
ShapeScale * kit = new ShapeScale;
kit->active = TRUE;
kit->projectedSize = 5.0f;
// create the marker
SoSeparator * markersep = new SoSeparator;
SoBaseColor * mat = new SoBaseColor;
mat->rgb.setValue(normalized_rand(), normalized_rand(), normalized_rand());
markersep->addChild(mat);
// marker shape should be unit size, with center in (0.0f, 0.0f, 0.0f)
SoCube * cube = new SoCube;
cube->width = 1.0f;
cube->height = 1.0f;
cube->depth = 1.0f;
markersep->addChild(cube);
kit->setPart("shape", markersep);
markerroot->addChild(kit);
return markerroot;
}
static void
event_cb(void * ud, SoEventCallback * n)
{
const SoMouseButtonEvent * mbe = (SoMouseButtonEvent *)n->getEvent();
if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 &&
mbe->getState() == SoButtonEvent::DOWN) {
SoQtExaminerViewer * viewer = (SoQtExaminerViewer *)ud;
SoRayPickAction rp(viewer->getViewportRegion());
rp.setPoint(mbe->getPosition());
rp.apply(viewer->getSceneManager()->getSceneGraph());
SoPickedPoint * point = rp.getPickedPoint();
if (point == NULL) {
(void)fprintf(stderr, "\n** miss! **\n\n");
return;
}
n->setHandled();
const SoPath * p = rp.getCurPath();
for (int i = 0; i < p->getLength(); i++) {
SoNode * n = p->getNodeFromTail(i);
if (n->isOfType(SoGroup::getClassTypeId())) {
SoGroup * g = (SoGroup *)n;
g->addChild(construct_new_marker(point->getPoint()));
break;
}
}
}
}
void
show_instructions(void)
{
(void)fprintf(stdout,
"\nThis example program demonstrates the use of the ShapeScale nodekit.\n"
"\nQuick instructions:\n\n"
" * place the marker by clicking on a shape with the left mouse button\n"
" * hit ESC to toggle back and forth to view mode\n"
" * zoom back and forth to see how the markers stay the same size\n\n");
}
int
main(int argc, char ** argv)
{
if (argc != 2) {
(void) fprintf(stderr,"\nSpecify an Inventor file as argument.\n");
return -1;
}
QWidget * window = SoQt::init(argv[0]);
ShapeScale::initClass(); // init our extension nodekit
SoQtExaminerViewer * ex1 = new SoQtExaminerViewer(window);
SoInput input;
SbBool ok = input.openFile(argv[1]);
if (!ok) {
(void) fprintf(stderr, "Unable to open file: %s\n", argv[1]);
return -1;
}
SoSeparator * root = SoDB::readAll(&input);
if (root == NULL) {
(void) fprintf(stderr, "Unable to read file: %s\n", argv[1]);
return -1;
}
show_instructions();
SoSeparator * newroot = new SoSeparator;
newroot->ref();
newroot->addChild(root);
// create event callback and marker nodes
SoSeparator * sep = new SoSeparator;
newroot->addChild(sep);
SoEventCallback * ecb = new SoEventCallback;
ecb->addEventCallback(SoMouseButtonEvent::getClassTypeId(), event_cb, ex1);
sep->addChild(ecb);
ex1->setSceneGraph(newroot);
ex1->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_BLEND);
ex1->setViewing(FALSE);
ex1->show();
SoQt::show(window);
SoQt::mainLoop();
delete ex1;
newroot->unref();
return 0;
}
\endcode
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include // coin_isspace()
#include // COIN_OBSOLETED()
#include "../io/SoWriterefCounter.h"
#include
class SoBaseKitP {
public:
SoBaseKitP(SoBaseKit * kit) : kit(kit) { }
SoBaseKit * kit;
SoFieldData * writedata;
SbBool didcount;
// This array is a 1-1 mapping of the fields corresponding to the
// catalog parts. Catalog indices will therefore also be used as
// indices into this array.
SbList instancelist;
void addKitDetail(SoFullPath * path, SoPickedPoint * pp);
void createWriteData(void);
void testParentWrite(void);
void copyParts(const SoBaseKit * srckit, SbList & partlist,
const SbBool copyconnections);
void setParts(SbList partlist, const SbBool leafparts);
SbBool readUnknownFields(SoInput *in, SoFieldData *&unknownFieldData );
};
#undef PRIVATE
#define PRIVATE(p) ((p)->pimpl)
#undef PUBLIC
#define PUBLIC(p) ((p)->kit)
SbBool SoBaseKit::searchchildren = FALSE;
SO_KIT_SOURCE(SoBaseKit);
/*!
\fn const SoNodekitCatalog * SoBaseKit::getClassNodekitCatalog(void)
Returns the nodekit catalog which defines the layout of this
class' kit.
*/
/*!
\fn const SoNodekitCatalog * SoBaseKit::getNodekitCatalog(void) const
Returns the nodekit catalog which defines the layout of this
class' kit.
*/
/*!
\fn const SoNodekitCatalog ** SoBaseKit::getClassNodekitCatalogPtr(void)
Returns the pointer to the pointer of the nodekit catalog
for this class.
*/
/*!
\var SoChildList * SoBaseKit::children
\COININTERNAL
*/
/*!
\var SbBool SoBaseKit::connectionsSetUp
\COININTERNAL
*/
/*!
Constructor.
This is the top-level superclass of all node kit and dragger
classes. The catalog structure of SoBaseKit is as follows:
\verbatim
CLASS SoBaseKit
-->"this"
--> "callbackList"
\endverbatim
\NODEKIT_POST_DIAGRAM
\NODEKIT_PRE_TABLE
\verbatim
CLASS SoBaseKit
PVT "this", SoBaseKit ---
"callbackList", SoNodeKitListPart [ SoCallback, SoEventCallback ]
\endverbatim
\NODEKIT_POST_TABLE
As can be seen from the catalog, all node kits can have a callback
node in front of all other nodes in the kit. This is handy for
catching events that should go to application processing.
*/
SoBaseKit::SoBaseKit(void)
{
PRIVATE(this) = new SoBaseKitP(this);
PRIVATE(this)->writedata = NULL;
SO_KIT_INTERNAL_CONSTRUCTOR(SoBaseKit);
// Can't use ADD_CATALOG_ENTRY macro for the toplevel "this" entry,
// as we don't want to call SO_NODE_ADD_FIELD(). This is how the
// invocation would have looked if we could use the macro:
//
// SO_KIT_ADD_CATALOG_ENTRY(this, SoBaseKit, TRUE, "", "", FALSE);
SoBaseKit::classcatalog->addEntry("this",
SoBaseKit::getClassTypeId(),
SoBaseKit::getClassTypeId(),
TRUE,
"",
"",
FALSE,
SoType::badType(),
SoType::badType(),
FALSE);
SO_KIT_ADD_CATALOG_LIST_ENTRY(callbackList, SoSeparator, TRUE, this, "", SoCallback, TRUE);
SO_KIT_ADD_LIST_ITEM_TYPE(callbackList, SoEventCallback);
// this could be created on demand, but will make it more complicated
this->children = new SoChildList(this);
this->connectionsSetUp = FALSE;
SO_KIT_INIT_INSTANCE();
}
/*!
Destructor.
*/
SoBaseKit::~SoBaseKit()
{
delete this->children;
delete PRIVATE(this);
}
// Doc in superclass
void
SoBaseKit::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoBaseKit, SO_FROM_INVENTOR_1);
// set rayPick method
SoType type = SoBaseKit::getClassTypeId();
SoRayPickAction::addMethod(type, SoNode::rayPickS);
SoAudioRenderAction::addMethod(type,
SoAudioRenderAction::callDoAction);
SoBaseKit::searchchildren = FALSE;
}
/*!
Returns a pointer to the node part with \a partname.
This method calls SoBaseKit::getAnyPart() with \a leafcheck and \a
publiccheck both set to \c TRUE.
See the documentation of SoBaseKit::getAnyPart() for information on
how to use \a partname and \a makeifneeded, and what you can expect
to get returned from this method.
*/
SoNode *
SoBaseKit::getPart(const SbName & partname, SbBool makeifneeded)
{
return this->getAnyPart(partname, makeifneeded, TRUE, TRUE);
}
/*!
Returns the full path name to a catalog part, given the part's
current item pointer.
*/
SbString
SoBaseKit::getPartString(const SoBase * part)
{
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
if (part->isOfType(SoNode::getClassTypeId())) {
int idx = this->findNodeInThisKit((SoNode *)part);
if (idx >= 0) {
return SbString(catalog->getName(idx).getString());
}
return SbString();
}
else if (part->isOfType(SoPath::getClassTypeId())) {
SoFullPath * path = (SoFullPath *)part;
int pathidx = path->findNode(this);
if (pathidx < 0) return SbString();
SoBaseKit * kit = this;
SbString partname;
int parentnum = 0;
SoNode * tail = path->getTail();
SoNode * node = kit;
while (node != tail) {
node = path->getNode(++pathidx);
int partnum = kit->findNodeInThisKit(node, parentnum);
if (partnum < 0) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::getPartString",
"Illegal path");
#endif // COIN_DEBUG
return SbString();
}
if (catalog->isLeaf(partnum)) {
if (partname != "") partname += '.';
partname += catalog->getName(partnum).getString();
}
if (node->isOfType(SoNodeKitListPart::getClassTypeId())) {
// no sense in using SoNodeKitListPart as a non-leaf node, right?
assert(catalog->isLeaf(partnum));
SoNodeKitListPart * list = (SoNodeKitListPart *)node;
pathidx += 2; // // skip container node
if (pathidx >= path->getLength()) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::getPartString",
"Path too short");
#endif // COIN_DEBUG
return SbString();
}
node = path->getNode(pathidx);
int childidx = list->findChild(node);
assert(childidx >= 0);
partname += '[';
partname.addIntString(childidx);
partname += ']';
}
if (node->isOfType(SoBaseKit::getClassTypeId())) {
kit = (SoBaseKit *) node;
catalog = kit->getNodekitCatalog();
parentnum = 0;
}
else {
// search more in this kit
parentnum = partnum;
}
}
return partname;
}
return SbString();
}
/*!
Calls SoBaseKit::createPathToAnyPart() with \a leafcheck \c FALSE,
and \a publiccheck \c FALSE (and other arguments as given to this
function).
See SoBaseKit::createPathToAnyPart() for documentation.
*/
SoNodeKitPath *
SoBaseKit::createPathToPart(const SbName & partname, SbBool makeifneeded, const SoPath * pathtoextend)
{
return this->createPathToAnyPart(partname, makeifneeded, TRUE, TRUE, pathtoextend);
}
/*!
Sets the catalog part given by \a partname to the \a from node pointer.
*/
SbBool
SoBaseKit::setPart(const SbName & partname, SoNode * from)
{
return this->setAnyPart(partname, from, FALSE);
}
static const char *
skip_spaces(const char * ptr)
{
// ANSI C isspace() takes the current locale into account. Under
// MSWindows, this can lead to "interesting" artifacts, like a case
// with RR tracked down and fixed by where a
// character (was it ü?) with ASCII value > 127 made isspace()
// return non-nil on a German system. So we're using our own
// locale-independent isspace() implementation instead.
while (coin_isspace(*ptr)) ptr++;
return ptr;
}
static int
find_partname_length(const char * ptr)
{
int cnt = 0;
while (ptr[cnt] && !coin_isspace(ptr[cnt]) &&
ptr[cnt] != '{' && ptr[cnt] != '}') {
cnt++;
}
return cnt;
}
/*!
Sets nodekit part field values. The input argument string is of the
format:
\code
partname {
fieldname fieldval
fieldname fieldval
[...]
}
partname {
fieldname fieldval
fieldname fieldval
[...]
}
[...]
\endcode
(Whitespace layout is ignored, as always for Inventor format input
strings.)
Here's an example, changing several values of the camera part of an
SoCameraKit instance:
\code
kit->set("camera { heightAngle 0.3927 nearDistance 1.1 farDistance 999.9 }");
\endcode
*/
SbBool
SoBaseKit::set(const char * namevaluepairliststring)
{
const size_t stringlen = strlen(namevaluepairliststring); // cache this value
const char * currptr = skip_spaces(namevaluepairliststring);
SoInput memInput;
while (*currptr) {
int partnamelen = find_partname_length(currptr);
const char * start = skip_spaces(currptr + partnamelen);
if (*start != '{') { // first non-space after partname should be a {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::set",
"parse error at byte %d in input string",
start-namevaluepairliststring);
#endif // COIN_DEBUG
return FALSE;
}
start++; // skip {
SbString partname(currptr, 0, partnamelen-1);
SoBaseKit * kit = this;
int partNum;
SbBool isList;
int listIdx;
if (!SoBaseKit::findPart(partname, kit, partNum, isList, listIdx, TRUE, NULL, TRUE)) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::set",
"part ``%s'' not found",
partname.getString());
#endif // COIN_DEBUG
return FALSE;
}
SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
PRIVATE(kit)->instancelist[partNum]->setDefault(FALSE);
if (isList) {
SoNodeKitListPart * list = (SoNodeKitListPart *)node;
if (listIdx < 0 || listIdx > list->getNumChildren()) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::set",
"index %d out of bounds for part ``%s''",
listIdx, partname.getString());
#endif // COIN_DEBUG
return FALSE;
}
else if (listIdx == list->getNumChildren()) {
if (!list->canCreateDefaultChild()) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::set",
"Unable to create default child for list-part ``%s''",
partname.getString());
#endif // COIN_DEBUG
return FALSE;
}
node = list->createAndAddDefaultChild();
}
else {
node = list->getChild(listIdx);
}
}
memInput.setBuffer((void *)start, stringlen - (start-namevaluepairliststring));
SbBool dummy;
if (!node->getFieldData()->read(&memInput, node, TRUE, dummy)) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::set",
"error while parsing data for part ``%s''",
partname.getString());
#endif // COIN_DEBUG
return FALSE;
}
currptr = start + (int) memInput.getNumBytesRead();
if (*currptr == '}') currptr++;
assert(currptr <= namevaluepairliststring + stringlen);
currptr = skip_spaces(currptr);
}
return TRUE;
}
/*!
This just overloads the other SoBaseKit::set() method, and provides
a way to set a part value by using a separate input argument for the
name of the part and the name of the field (i.e. parameter)
settings.
*/
SbBool
SoBaseKit::set(const char * partnamestring, const char * parameterstring)
{
SbString partname(partnamestring);
int partNum;
SbBool isList;
int listIdx;
SoBaseKit * kit = this;
if (SoBaseKit::findPart(partname, kit, partNum, isList, listIdx, TRUE, NULL, TRUE)) {
SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
PRIVATE(kit)->instancelist[partNum]->setDefault(FALSE);
assert(node != NULL); // makeifneeded was TRUE in findPart call
if (isList) {
assert(node->isOfType(SoNodeKitListPart::getClassTypeId()));
SoNodeKitListPart * list = (SoNodeKitListPart *) node;
if (listIdx < 0 || listIdx > list->getNumChildren()) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::set",
"index %d out of bounds for part ``%s''",
listIdx, partnamestring);
#endif // COIN_DEBUG
return FALSE;
}
else if (listIdx == list->getNumChildren()) {
if (!list->canCreateDefaultChild()) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::set",
"Unable to create default child for list-part ``%s''",
partname.getString());
#endif // COIN_DEBUG
return FALSE;
}
node = list->createAndAddDefaultChild();
}
else {
node = list->getChild(listIdx);
}
}
if (node) {
SoInput memInput;
SbBool dummy;
memInput.setBuffer((void *)parameterstring, strlen(parameterstring));
const SoFieldData * fielddata = node->getFieldData();
return fielddata->read(&memInput, node, TRUE, dummy);
}
}
return FALSE;
}
// Doc in superclass.
void
SoBaseKit::doAction(SoAction * action)
{
int numindices;
const int * indices;
if (action->getPathCode(numindices, indices) == SoAction::IN_PATH) {
this->children->traverseInPath(action, numindices, indices);
}
else {
this->children->traverse(action);
}
}
// Doc in superclass.
void
SoBaseKit::callback(SoCallbackAction * action)
{
SoBaseKit::doAction((SoAction *)action);
}
// Doc in superclass.
void
SoBaseKit::GLRender(SoGLRenderAction * action)
{
SoBaseKit::doAction((SoAction *)action);
}
// Doc in superclass. Overriden to calculate bounding box center.
void
SoBaseKit::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++) {
this->children->traverse(action, i, i);
if (action->isCenterSet()) {
acccenter += action->getCenter();
numacc++;
action->resetCenter();
}
}
if (numacc) action->setCenter(acccenter / float(numacc), FALSE);
}
// Doc in superclass.
void
SoBaseKit::getMatrix(SoGetMatrixAction * action)
{
// SoBaseKit should be travesed like a normal SoGroup node, and the
// children should only be traversed if we're IN_PATH or OFF_PATH
// (SoGetMatrixAction is only applied on a path or on a single node,
// and we must not calculate when BELOW_PATH or NO_PATH).
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;
default:
break;
}
}
// Doc in superclass.
void
SoBaseKit::handleEvent(SoHandleEventAction * action)
{
SoBaseKit::doAction((SoAction *)action);
}
// Doc in superclass.
void
SoBaseKit::rayPick(SoRayPickAction * action)
{
SoBaseKit::doAction((SoAction *)action);
const SoPickedPointList & pplist = action->getPickedPointList();
const int n = pplist.getLength();
for (int i = 0; i < n; i++) {
SoPickedPoint * pp = pplist[i];
SoFullPath * path = (SoFullPath*) pp->getPath();
if (path->containsNode(this) && pp->getDetail(this) == NULL) {
PRIVATE(this)->addKitDetail(path, pp);
}
}
}
// Doc in superclass.
void
SoBaseKit::search(SoSearchAction * action)
{
inherited::search(action);
if (action->isFound() || !SoBaseKit::searchchildren) return;
SoBaseKit::doAction((SoAction *)action);
}
// Test if node has all fields set to default and if the fields
// contains the default values. If so, we don't need to write it.
static SbBool
is_default_node(SoNode * node)
{
SoNode * definstance = NULL;
const SoFieldData * fielddata = node->getFieldData();
int i, n = fielddata->getNumFields();
for (i = 0; i < n; i++) {
SoField * field = fielddata->getField(node, i);
if (field->isConnectionEnabled() && field->isConnected()) break;
if (definstance == NULL) {
definstance = (SoNode *)node->getTypeId().createInstance();
definstance->ref();
}
if (!field->isDefault() &&
!field->isSame(*fielddata->getField(definstance, i))) break;
}
if (definstance) definstance->unref();
// if all fields were tested, it is a default node.
return i == n;
}
// Doc in superclass.
void
SoBaseKit::write(SoWriteAction * action)
{
// debugging code start **************************************************
// If the below envvar is set, we'll write nodekit's current scene
// graph instead of writing as a nodekit.
//
// Note that if the nodekit is a dragger, the resulting scene graph
// export may still not look exactly the same as when the exported
// sub-graph is contained within the dragger, as
// SoDragger::GLRender() sets a number of elements in the traversal
// state to non-intrusive "default" values before rendering the
// dragger geometry. These settings will not be part of the exported
// iv-file.
static int dump = -1;
if (dump == -1) {
const char * env = coin_getenv("COIN_DEBUG_FLATTEN_NODEKITS_ON_WRITE");
dump = env && (atoi(env) > 0);
}
if (dump) {
this->children->traverse(action);
return;
}
// debugging code end ****************************************************
SoOutput * out = action->getOutput();
if (out->getStage() == SoOutput::COUNT_REFS) {
this->addWriteReference(out, FALSE);
}
else if (out->getStage() == SoOutput::WRITE) {
if (this->writeHeader(out, FALSE, FALSE)) return; // no more to write
// FIXME: shouldn't this if() rather be an assert? 20030523 mortene.
if (PRIVATE(this)->writedata) {
PRIVATE(this)->writedata->write(out, this);
// we don't need it any more
delete PRIVATE(this)->writedata;
PRIVATE(this)->writedata = NULL;
}
this->writeFooter(out);
}
else assert(0 && "unknown stage");
}
// documented in superclass
void
SoBaseKit::addWriteReference(SoOutput * out, SbBool isfromfield)
{
// don't call inherited::addWriteReference(), as we will handle
// the fields ourselves, using a new fielddata. This is needed to
// write fields in the correct order.
SoBase::addWriteReference(out, isfromfield);
// If first invocation during the reference counting pass, check
// nodes in our catalog.
if (!isfromfield && !SoWriterefCounter::instance(out)->hasMultipleWriteRefs(this)) {
this->countMyFields(out);
}
}
/*!
Reference count the write connections to nodes in the catalog.
*/
void
SoBaseKit::countMyFields(SoOutput * out)
{
assert(out->getStage() == SoOutput::COUNT_REFS);
// already created?
//
// FIXME: could this ever be TRUE without that being an error
// situation? I have a feeling this should rather be an
// assert(). Investigate. 20030523 mortene.
if (PRIVATE(this)->writedata) return;
// Initialize isDefault() flag on fields that should not be
// written. This is a virtual method.
this->setDefaultOnNonWritingFields();
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
// PRIVATE(this)->writedata contains a sorted list of fields.
//
// FIXME: the pimpl->writedata scheme doesn't look multithread-safe
// wrt multiple SoWriteAction instances working in parallel over the
// same scene. 20030521 mortene.
PRIVATE(this)->createWriteData();
// test if parent of parts is writing. Then we must write part anyway.
PRIVATE(this)->testParentWrite();
// we might count fields that won't be written here, but it
// doesn't matter, since we're operating on a copy of the fields.
int i, n = PRIVATE(this)->writedata->getNumFields();
for (i = 0; i < n; i++) {
const SbName name = PRIVATE(this)->writedata->getFieldName(i);
SoField * field = PRIVATE(this)->writedata->getField(this, i);
int partnum = catalog->getPartNumber(name);
if (partnum < 0) {
// field is not a part. Do normal field write.
if (field->shouldWrite()) {
field->write(out, name);
}
}
else {
if (!field->isDefault()) field->write(out, name);
else {
SoNode * node = (SoNode*) ((SoSFNode*)field)->getValue();
if (node) {
if (node->isOfType(SoBaseKit::getClassTypeId())) {
SoBaseKit * kit = (SoBaseKit*) node;
kit->countMyFields(out);
if (kit->forceChildDrivenWriteRefs(out)) {
field->setDefault(FALSE);
// add a write reference on the kit node only. We supply
// isfromfield TRUE to achieve this
kit->addWriteReference(out, TRUE);
}
}
}
}
}
}
}
// Note: the following documentation for
// setDefaultOnNonWritingFields() will also be used for nodekit and
// dragger subclasses, so keep it general.
/*!
(Be aware that this method is unlikely to be of interest to the
application programmer who does not want to extend the library with
new custom nodekits or draggers. If you indeed \e are writing
extensions, see the information in the SoBaseKit class
documentation.)
This is a virtual method, and the code in it should call
SoField::setDefault() with argument \c TRUE on part fields that
should not be written upon scenegraph export operations.
This is typically done when:
- field value is \c NULL and part is \c NULL by default
- it is a leaf SoGroup or SoSeparator node with no children
- it is a leaf listpart with no children and an SoGroup or
SoSeparator container
- it is a non-leaf part and it's of SoGroup type and all fields
are at their default values
Subclasses should usually override this to do additional settings
for new member fields. From the subclass, do remember to call
"upwards" to your superclass' setDefaultOnNonWritingFields() method.
*/
void
SoBaseKit::setDefaultOnNonWritingFields(void)
{
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
int n = PRIVATE(this)->instancelist.getLength();
for (int i = 1; i < n; i++) {
SoSFNode * field = PRIVATE(this)->instancelist[i];
if (field->isDefault()) { continue; }
SoNode * node = field->getValue();
if (node == NULL) {
// first test listed in API doc above
if (catalog->isNullByDefault(i)) { field->setDefault(TRUE); }
continue;
}
const SbBool leaf = catalog->isLeaf(i);
const SoType type = node->getTypeId();
if (leaf) {
// second test
if ((type == SoGroup::getClassTypeId() ||
type == SoSeparator::getClassTypeId()) &&
((SoGroup*)node)->getNumChildren() == 0) {
field->setDefault(TRUE);
}
// third test
else if (type == SoNodeKitListPart::getClassTypeId()) {
SoNodeKitListPart * list = (SoNodeKitListPart*) node;
const SoNode * container = list->getContainerNode();
if (list->getNumChildren() == 0 && container &&
(container->getTypeId() == SoSeparator::getClassTypeId() ||
container->getTypeId() == SoGroup::getClassTypeId())) {
field->setDefault(TRUE);
}
}
}
else { // not leaf
// fourth test
if (node->isOfType(SoGroup::getClassTypeId()) && is_default_node(node)) {
field->setDefault(TRUE);
}
}
}
}
/*!
Returns \c TRUE if kit should write. This happens if shouldWrite()
returns \c TRUE, or if any of the children (recursively) should
write.
*/
SbBool
SoBaseKit::forceChildDrivenWriteRefs(SoOutput * out)
{
if (SoWriterefCounter::instance(out)->shouldWrite(this)) return TRUE;
// if NULL we already did this test, found that we shouldn't write,
// deleted writedata and set writedata to NULL.
if (!PRIVATE(this)->writedata) return FALSE;
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
int i, n = PRIVATE(this)->writedata->getNumFields();
// loop through fields and break as soon as we find a reason
// to write
for (i = 0; i < n; i++) {
SoField * field = PRIVATE(this)->writedata->getField(this, i);
int partnum = catalog->getPartNumber(PRIVATE(this)->writedata->getFieldName(i));
if (!field->isDefault()) break;
else if (partnum < 0 && field->isIgnored()) break;
else if (partnum > 0) {
SoSFNode * part = (SoSFNode*) field;
SoNode * node = part->getValue();
if (node) {
if (SoWriterefCounter::instance(out)->shouldWrite(node)) break;
else if (node->isOfType(SoBaseKit::getClassTypeId())) {
SoBaseKit * kit = (SoBaseKit*) node;
// recurse
if (kit->forceChildDrivenWriteRefs(out)) break;
}
}
}
}
if (i < n) { // did we find a reason to write?
SoBase::addWriteReference(out);
return TRUE;
}
else {
delete PRIVATE(this)->writedata;
PRIVATE(this)->writedata = NULL;
return FALSE;
}
}
// Documented in superclass.
void
SoBaseKit::getPrimitiveCount(SoGetPrimitiveCountAction * action)
{
SoBaseKit::doAction((SoAction *)action);
}
// Documented in superclass.
SoChildList *
SoBaseKit::getChildren(void) const
{
return this->children;
}
/*!
Print out the full nodekit catalog structure. Just invokes
SoBaseKit::printSubDiagram() on the catalog root. Useful for
debugging.
Example output:
\verbatim
CLASS SoWrapperKit
-->"this"
"callbackList"
"topSeparator"
"pickStyle"
"appearance"
"units"
"transform"
"texture2Transform"
"childList"
--> "localTransform"
--> "contents"
\endverbatim
The arrows denote new entries in the catalog for the particular
class versus it's superclass. (Apart from the root entry, of
course.)
For a more detailed catalog dump, see SoBaseKit::printTable().
*/
void
SoBaseKit::printDiagram(void)
{
fprintf(stdout, "CLASS So%s\n", this->getTypeId().getName().getString());
this->printSubDiagram("this", 0);
}
/*!
Print out the nodekit catalog structure from \a rootname and
downwards in the catalog tree, with indentation starting at \a
level.
\sa printDiagram()
*/
void
SoBaseKit::printSubDiagram(const SbName & rootname, int level)
{
const SoNodekitCatalog * parentcatalog = NULL;
if (this->getTypeId() != SoBaseKit::getClassTypeId()) {
SoType parenttype = this->getTypeId().getParent();
SoBaseKit * parentobj = (SoBaseKit *)parenttype.createInstance();
parentcatalog = parentobj->getNodekitCatalog();
parentobj->ref();
parentobj->unref();
}
const SoNodekitCatalog * thiscat = this->getNodekitCatalog();
int i = 0;
if (!parentcatalog ||
parentcatalog->getPartNumber(rootname) == SO_CATALOG_NAME_NOT_FOUND ||
parentcatalog->getType(rootname) != thiscat->getType(rootname)) {
fprintf(stdout, "-->");
i++;
}
for (; i < level+1; i++) fprintf(stdout, " ");
fprintf(stdout, "\"%s\"\n", rootname.getString());
for (int j=0; j < thiscat->getNumEntries(); j++) {
if (thiscat->getParentName(j) == rootname)
this->printSubDiagram(thiscat->getName(j), level + 1);
}
}
/*!
Write the complete nodekit catalog in table form.
Example output:
\verbatim
CLASS SoWrapperKit
PVT "this", SoWrapperKit ---
"callbackList", SoNodeKitListPart [ SoCallback, SoEventCallback ]
PVT "topSeparator", SoSeparator ---
"pickStyle", SoPickStyle ---
"appearance", SoAppearanceKit ---
"units", SoUnits ---
"transform", SoTransform ---
"texture2Transform", SoTexture2Transform ---
"childList", SoNodeKitListPart [ SoShapeKit, SoSeparatorKit ]
"localTransform", SoTransform ---
"contents", SoSeparator ---
\endverbatim
\c PVT denotes that it's a private entry in the catalog, then
follows the part name and the part type. If the part is a list, the
allowed node types for the list is given in square brackets, and if
not there's a triple hyphen. If the part type is abstract, the
default part type will be listed last (not shown in the example
output above).
*/
void
SoBaseKit::printTable(void)
{
fprintf(stdout, "CLASS So%s\n", this->getTypeId().getName().getString());
const SoNodekitCatalog * thiscat = this->getNodekitCatalog();
for (int i=0; i < thiscat->getNumEntries(); i++) {
const SoType t = thiscat->getType(i);
fprintf(stdout, "%s \"%s\", So%s ",
thiscat->isPublic(i) ? " " : "PVT",
thiscat->getName(i).getString(),
t.getName().getString());
if (thiscat->isList(i)) {
SoTypeList tlist = thiscat->getListItemTypes(i);
fprintf(stdout, "[ ");
for (int j=0; j < tlist.getLength(); j++) {
if (j) fprintf(stdout, ", ");
fprintf(stdout, "So%s", tlist[j].getName().getString());
}
fprintf(stdout, " ] ");
}
else {
fprintf(stdout, " --- ");
}
if (t != thiscat->getDefaultType(i)) {
fprintf(stdout, ", (default type = So%s)",
thiscat->getDefaultType(i).getName().getString());
}
fprintf(stdout, "\n");
}
}
/*!
Returns the value of the flag indicating whether or not the kit
parts are searched during SoSearchAction traversal.
\sa SoBaseKit::setSearchingChildren()
*/
SbBool
SoBaseKit::isSearchingChildren(void)
{
return SoBaseKit::searchchildren;
}
/*!
Set whether or not the kit parts should be searched during
SoSearchAction traversal. The default value is \c FALSE.
*/
void
SoBaseKit::setSearchingChildren(const SbBool newval)
{
SoBaseKit::searchchildren = newval;
}
/*!
\COININTERNAL
This is a private method in the original SGI Inventor 2.1
API. Consider it obsolete -- it will be removed from Coin v3.0.
*/
SoNode *
SoBaseKit::typeCheck(const SbName & partname, const SoType & parttype,
SoNode * node)
{
if (node == NULL) {
#if COIN_DEBUG
SoDebugError::postInfo("SoBaseKit::typeCheck",
"node was NULL");
#endif // COIN_DEBUG
return NULL;
}
if (!node->isOfType(parttype)) {
#if COIN_DEBUG
SoDebugError::postInfo("SoBaseKit::typeCheck",
"wrong type: %s", node->getTypeId().getName().getString());
#endif // COIN_DEBUG
return NULL;
}
return node;
}
// Documented in superclass. Overridden to also recurse on non-null
// part nodes.
SoNode *
SoBaseKit::addToCopyDict(void) const
{
SoNode * cp = (SoNode*) SoFieldContainer::checkCopy(this);
if (cp == NULL) { // not copied?
cp = (SoNode*) this->getTypeId().createInstance();
assert(cp);
cp->ref();
SoFieldContainer::addCopy(this, cp);
cp->unrefNoDelete();
int n = PRIVATE(this)->instancelist.getLength();
for (int i = 1; i < n; i++) {
SoNode * node = PRIVATE(this)->instancelist[i]->getValue();
if (node != NULL) node->addToCopyDict();
}
}
return cp;
}
// (Doc in superclass.) Overridden to copy parts correctly.
void
SoBaseKit::copyContents(const SoFieldContainer * fromfc,
SbBool copyconnections)
{
int i;
// disable connections while copying
SbBool oldsetup = this->setUpConnections(FALSE);
// do normal node copy
inherited::copyContents(fromfc, copyconnections);
const SoBaseKit * srckit = (const SoBaseKit*) fromfc;
// convenient reference
const SbList & srcfields = srckit->getCatalogInstances();
const int n = PRIVATE(this)->instancelist.getLength();
// use temporary lists to store part node pointers and field
// default flag, as we will modify the originals.
SbList partlist;
SbList flaglist;
// part 0 is this
partlist.append(NULL);
flaglist.append(FALSE);
// initialize temporary lists
for (i = 1; i < n; i++) {
partlist.append(NULL);
flaglist.append(PRIVATE(this)->instancelist[i]->isDefault());
}
// copy parts, taking care of scene graph
PRIVATE(this)->copyParts(srckit, partlist, copyconnections);
// remove all old children before setting parts again
this->getChildren()->truncate(0);
// reset part fields
for (i = 1; i < n; i++) {
PRIVATE(this)->instancelist[i]->setValue(NULL);
PRIVATE(this)->instancelist[i]->setDefault(TRUE);
}
// set non-leaf nodes first
PRIVATE(this)->setParts(partlist, FALSE);
// then leaf nodes
PRIVATE(this)->setParts(partlist, TRUE);
// do final pass
for (i = 1; i < n; i++) {
// restore default flag for fields
PRIVATE(this)->instancelist[i]->setDefault(flaglist[i]);
// unref nodes in temporary list as they were ref'ed
// when inserted
if (partlist[i]) partlist[i]->unref();
}
// enable connections
if (oldsetup) this->setUpConnections(TRUE);
}
/*!
Returns a pointer to the group node above an SoNodeKitListPart in
the catalog given by \a listname.
If the list part (and its container) was not yet constructed, they
will be so if \a makeifneeded is \c TRUE (otherwise, \c NULL will be
returned).
*/
SoGroup *
SoBaseKit::getContainerNode(const SbName & listname, SbBool makeifneeded)
{
SoBaseKit * kit = this;
int partNum;
SbBool isList;
int listIdx;
if (SoBaseKit::findPart(SbString(listname.getString()), kit, partNum,
isList, listIdx, makeifneeded, NULL, TRUE)) {
SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
if (node == NULL) return NULL;
assert(node->isOfType(SoNodeKitListPart::getClassTypeId()));
SoNodeKitListPart * list = (SoNodeKitListPart *)node;
return list->getContainerNode();
}
return NULL;
}
/*!
Returns catalog part of the given \a partname.
If the \a partname part is not in the nodekit's catalog, return \c
NULL.
If the part is specified in the catalog, but has not yet been made,
the function will either construct the part (if \a makeifneeded is
\c TRUE) or just return \c NULL (if \a makeifneeded is \c FALSE).
If \a leafcheck is \c TRUE, a pointer to the part will only be
returned if it's a leaf in the catalog (otherwise \c NULL is
returned).
If \a publiccheck is \c TRUE, a pointer to the part will only be
returned if it's a public catalog part (otherwise \c NULL is
returned).
The \a partname input argument should be given as a \e "path" of
catalog part names down to the wanted leaf part. The syntax for
specifiying \a partname "paths" is as follows (given in Backus-Naur
Form (BNF)):
\verbatim
BNF:
partname = singlename | compoundname
compoundname = singlename | compoundname.singlename
singlename = singlepartname | singlelistelementname
singlelistelementname = singlelistname[idx]
singlepartname is name of a part ("ordinary", nodekit or list)
singlelistname is name of a part which is a list
idx is an integer value
\endverbatim
*/
SoNode *
SoBaseKit::getAnyPart(const SbName & partname, SbBool makeifneeded,
SbBool leafcheck, SbBool publiccheck)
{
SoBaseKit * kit = this;
int partNum;
SbBool isList;
int listIdx;
SbString partstring(partname.getString());
if (SoBaseKit::findPart(partstring, kit, partNum, isList, listIdx,
makeifneeded, NULL, TRUE)) {
if (publiccheck && !kit->getNodekitCatalog()->isPublic(partNum)) {
SoDebugError::postWarning("SoBaseKit::getAnyPart",
"Part ``%s'' found in %s, but access is private.",
partname.getString(),
this->getTypeId().getName().getString());
return NULL;
}
if (!leafcheck || kit->getNodekitCatalog()->isLeaf(partNum)) {
if (isList) {
SoNode * partnode = PRIVATE(kit)->instancelist[partNum]->getValue();
if (partnode == NULL) return NULL;
assert(partnode->isOfType(SoNodeKitListPart::getClassTypeId()));
SoNodeKitListPart * list = (SoNodeKitListPart *) partnode;
if (listIdx >= 0 && listIdx < list->getNumChildren()) {
return list->getChild(listIdx);
}
else if (makeifneeded && (listIdx == list->getNumChildren())) {
if (!list->canCreateDefaultChild()) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::getAnyPart",
"Unable to create default child for list-part ``%s''",
partname.getString());
#endif // COIN_DEBUG
}
return list->createAndAddDefaultChild();
}
else {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::getAnyPart",
"index %d out of bounds for part ``%s''",
listIdx, partname.getString());
#endif // COIN_DEBUG
}
}
else {
return PRIVATE(kit)->instancelist[partNum]->getValue();
}
}
}
// FIXME:
// run cleanup?, in case some node has been temporarily created while
// searching for the part?? pederb, 2000-01-05
#if COIN_DEBUG
if (makeifneeded) { // user probably expected part to be found, post a warning
SoDebugError::postWarning("SoBaseKit::getAnyPart",
"part ``%s'' not found in %s",
partname.getString(),
this->getTypeId().getName().getString());
}
#endif // COIN_DEBUG
return NULL;
}
/*!
Return path with nested SoNodeKit instances down in the catalog
hierarchy given by \a partname.
If the trailing part has not been made and \a makeifneeded is \c
TRUE, make an instance of the part type and insert into the catalog,
as done in setAnyPart().
If \a leafcheck is \c TRUE, ignore non-leaf catalog node entries. If
\a publiccheck is \c TRUE, ignore private catalog entries.
\a pathtoextend is a path through the nodekit instance catalog
hierarchy, where we should pick up and continue to create the path
from where \a pathtoextend terminates. If \a pathtoextend is \c
NULL, we simply start at the "this" toplevel node.
Returns \c NULL on failure, for any of the possible reasons
described above (part ends in non-leaf or private catalog entry,
part is not syntactically valid or refers to non-existing catalog
entries).
*/
SoNodeKitPath *
SoBaseKit::createPathToAnyPart(const SbName & partname, SbBool makeifneeded,
SbBool leafcheck, SbBool publiccheck,
const SoPath * pathtoextend)
{
SoFullPath * path;
if (pathtoextend) {
path = (SoFullPath *)pathtoextend->copy();
// pop off nodes beyond this kit node
if (path->containsNode(this)) while (path->getTail() != this && path->getLength()) path->pop();
else if (path->getLength()) {
SoNode * node = path->getTail();
if (!node->getChildren() || node->getChildren()->find(this) < 0) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::createPathToAnyPart",
"pathtoextend is illegal");
#endif // COIN_DEBUG
return NULL;
}
path->append(this); // this should be safe now
}
}
else {
path = (SoFullPath *)new SoPath(this);
}
path->ref();
SoBaseKit * kit = this;
int partNum;
SbBool isList;
int listIdx;
if (SoBaseKit::findPart(SbString(partname.getString()), kit, partNum,
isList, listIdx, makeifneeded, path)) {
const SoNodekitCatalog * catalog = kit->getNodekitCatalog();
if ((leafcheck && ! catalog->isLeaf(partNum)) ||
(publiccheck && ! catalog->isPublic(partNum))) {
path->unref();
return NULL;
}
SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
if (node) {
path->append(node);
if (isList) {
SoNodeKitListPart * list = (SoNodeKitListPart *)node;
int numlistchildren = list->getNumChildren();
if (listIdx < 0 || listIdx > numlistchildren || (!makeifneeded && listIdx == numlistchildren)) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::createPathToAnyPart",
"index %d out of bounds for part ``%s''",
listIdx, partname.getString());
#endif // COIN_DEBUG
path->unref();
return NULL;
}
else if (listIdx == numlistchildren) {
if (!list->canCreateDefaultChild()) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::createPathToAnyPart",
"Unable to create default child for list-part ``%s''",
partname.getString());
#endif //COIN_DEBUG
}
else {
SoNode * newnode = list->createAndAddDefaultChild();
path->append(list->getContainerNode());
path->append(newnode);
}
}
else {
path->append(list->getContainerNode());
path->append(list->getChild(listIdx));
}
}
path->unrefNoDelete();
return (SoNodeKitPath *)path;
}
}
path->unref();
return NULL;
}
/*!
\COININTERNAL
*/
SbBool
SoBaseKit::setAnyPart(const SbName & partname, SoNode * from, SbBool anypart)
{
SoBaseKit * kit = this;
int partNum;
SbBool isList;
int listIdx;
SbString partstring(partname.getString());
// FIXME: findPart() really needs another parameter, since we need
// to create intermediate parts, but not the leaf part. For now we
// just supply makeifneeded = TRUE, and then immediately overwrite
// the part here. pederb, 2004-06-07
if (SoBaseKit::findPart(partstring, kit, partNum, isList, listIdx, TRUE, NULL, TRUE)) {
if (anypart || kit->getNodekitCatalog()->isPublic(partNum)) {
if (isList) {
SoNode * partnode = PRIVATE(kit)->instancelist[partNum]->getValue();
if (partnode) {
assert(partnode->isOfType(SoNodeKitListPart::getClassTypeId()));
SoNodeKitListPart * list = (SoNodeKitListPart *) partnode;
if (listIdx >= 0 && listIdx <= list->getNumChildren()) {
if (listIdx == list->getNumChildren())
list->addChild(from);
else
list->replaceChild(listIdx, from);
return TRUE;
}
else {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::setAnyPart",
"index %d out of bounds for part ``%s''",
listIdx, partname.getString());
#endif // COIN_DEBUG
}
}
}
else {
return kit->setPart(partNum, from);
}
}
}
// FIXME:
// run cleanup, in case some node has been temporarily created while
// searching for the part?? pederb, 2000-01-05
return FALSE;
}
/*!
Not part of the Coin API.
It is supposed to create the SoNodekitParts class instance. Since
this class can only be used by SoBaseKit (all members are private,
with SoBaseKit as friend), we decided to not support this class, and
solve the problem of recording which parts are created in an
alternative way.
*/
void
SoBaseKit::createNodekitPartsList(void)
{
assert(0 &&
"SoBaseKit::createNodekitPartsList() should not be used with Coin");
}
/*!
Replaces the createNodekitPartsList() method.
Sets up the list of SoSFNode fields with node pointers to the
instances in our catalog.
*/
void
SoBaseKit::createFieldList(void)
{
// FIXME:
// is there any way to make sure this code is only run once, and in
// the top level constructor. pederb, 2000-01-06
//
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
// only do this if the catalog has been created
if (catalog) {
PRIVATE(this)->instancelist.truncate(0);
PRIVATE(this)->instancelist.append(NULL); // first catalog entry is "this"
for (int i = 1; i < catalog->getNumEntries(); i++) {
PRIVATE(this)->instancelist.append((SoSFNode *)this->getField(catalog->getName(i)));
assert(PRIVATE(this)->instancelist[i] != NULL);
}
}
}
/*!
\COININTERNAL
*/
void
SoBaseKit::createDefaultParts(void)
{
// FIXME:
// is there any way to make sure this code is only run once, and in
// the top level constructor. pederb, 2000-01-06
//
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
// only do this if the catalog has been created
if (catalog) {
for (int i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
if (PRIVATE(this)->instancelist[i]->getValue() == NULL && !catalog->isNullByDefault(i)) {
this->makePart(i);
PRIVATE(this)->instancelist[i]->setDefault(TRUE);
}
}
}
}
/*!
In Open Inventor, this method returns a pointer to a private class.
It will always return \c NULL in Coin.
\sa createNodekitPartsList()
*/
const SoNodekitParts *
SoBaseKit::getNodekitPartsList(void) const
{
assert(0 &&
"SoBaseKit::getNodekitPartsList() obsoleted in Coin");
return NULL;
}
/*!
\COININTERNAL
*/
const SbList &
SoBaseKit::getCatalogInstances(void) const
{
// return this->fieldList;
return PRIVATE(this)->instancelist;
}
/*!
Obsoleted from the API in Coin.
*/
void
SoBaseKit::catalogError(void)
{
COIN_OBSOLETED();
}
// Note: the following documentation for setUpConnections() will also
// be visible for subclass nodekits and draggers, so keep it general.
/*!
Sets up all internal connections for instances of this class.
(This method will usually not be of interest to the application
programmer, unless you want to extend the library with new custom
nodekits or dragger classes. If so, see the SoBaseKit class
documentation.)
*/
SbBool
SoBaseKit::setUpConnections(SbBool onoff, SbBool doitalways)
{
return this->connectionsSetUp;
}
// doc in super
SbBool
SoBaseKit::readInstance(SoInput * in, unsigned short flags)
{
int i;
SbBool oldnotify = this->enableNotify(FALSE);
SbBool oldsetup = this->setUpConnections(FALSE);
// store old part values to find which parts are read
SoNodeList nodelist;
SbList defaultlist;
const SoNodekitCatalog * cat = this->getNodekitCatalog();
// Dummy first element to get indices to match instancelist (where
// the dummy "this" catalog entry is first).
nodelist.append(NULL);
defaultlist.append(FALSE);
// copy all parts into nodelist, and then set all parts to NULL
// and default before reading
for (i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
nodelist.append(PRIVATE(this)->instancelist[i]->getValue());
defaultlist.append(PRIVATE(this)->instancelist[i]->isDefault());
PRIVATE(this)->instancelist[i]->setValue(NULL);
PRIVATE(this)->instancelist[i]->setDefault(TRUE);
}
// reset the node kit by removing all children. We will restore it
// by setting the parts again later
this->getChildren()->truncate(0);
// actually read the nodekit.
// Use readUnknownFields instead to read fields not part of catalog
// SbBool ret = inherited::readInstance(in, flags);
// Fields that's not part of catalog is read as a SoSFNode, and stored
// in unknownfielddata. Later they'll be put in nodekit using setAnyPart.
SbBool ret = TRUE;
SoFieldData * unknownfielddata = new SoFieldData;
if (!PRIVATE(this)->readUnknownFields(in, unknownfielddata))
ret = FALSE;
if (ret) {
// loop through fields and copy the read parts into nodelist
for (i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
if (!PRIVATE(this)->instancelist[i]->isDefault()) { // we've read a part
nodelist.set(i, PRIVATE(this)->instancelist[i]->getValue());
defaultlist[i] = FALSE;
// set to NULL again so that setPart() will not get confused
PRIVATE(this)->instancelist[i]->setValue(NULL);
}
}
// restore the nodekit with all old and read parts
for (i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
if (!cat->isLeaf(i) && nodelist[i]) {
// if not leaf, remove all children. They will be re-added
// later when the children parts are set.
assert(nodelist[i]->isOfType(SoGroup::getClassTypeId()));
SoGroup * g = (SoGroup*) nodelist[i];
g->removeAllChildren();
}
this->setPart(i, nodelist[i]);
PRIVATE(this)->instancelist[i]->setDefault(defaultlist[i]);
}
// put the unknown fields into nodekit using setAnyPart
SbName partname;
SoNode * pnode;
SoSFNode * pfield;
for (i = 0; i < unknownfielddata->getNumFields(); i++) {
partname = unknownfielddata->getFieldName(i);
pfield = (SoSFNode *) unknownfielddata->getField(this, i);
pnode = pfield->getValue();
this->setAnyPart(partname, pnode);
}
}
delete unknownfielddata;
(void) this->setUpConnections(oldsetup);
(void) this->enableNotify(oldnotify);
return ret;
}
//
// recurse until not possible to split string any more, and return information
// about part and the kit the part is found in.
// Remember to set kit=this before calling this method, also remember that
// kit might change during this search.
//
// compoundname parts are created during this search, so it might be necessary
// to do a nodekit cleanup if part is not public, or if part is set to NULL.
//
//
// if path != NULL, kit-nodes will be appended to the path during the search
// The actual part is not added to the path. The head of the path should
// be set to the kit-node performing the search.
//
SbBool
SoBaseKit::findPart(const SbString & partname, SoBaseKit *& kit, int & partnum,
SbBool & islist, int & listidx, const SbBool makeifneeded,
SoPath * path, const SbBool recsearch)
{
// BNF:
//
// partname = singlename | compoundname
// compoundname = singlename | compoundname.singlename
// singlename = singlepartname | singlelistelementname
// singlelistelementname = singlelistname[idx]
//
// singlepartname is name of a part ("ordinary", nodekit or list)
// singlelistname is name of a part which is a list
// idx is an integer value
if (partname == "this") {
islist = FALSE;
partnum = 0;
return TRUE;
}
const char * stringptr = partname.getString();
const char * periodptr = strchr(stringptr, '.'); // find first period
const char * startbracket = strchr(stringptr, '[');
if (periodptr && (startbracket > periodptr))
startbracket = NULL; // will handle later
islist = FALSE; // set to FALSE first
SbString firstpartname;
if (startbracket) { // get index
long int listindex = strtol(startbracket+1, NULL, 10);
if (listindex == LONG_MIN || listindex == LONG_MAX) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::findPart",
"list index not properly specified");
#endif // COIN_DEBUG
return FALSE;
}
const ptrdiff_t endidx = startbracket - stringptr - 1;
firstpartname = partname.getSubString(0, (int)endidx);
listidx = (int) listindex;
islist = TRUE;
}
else if (periodptr) {
const ptrdiff_t endidx = periodptr - stringptr - 1;
firstpartname = partname.getSubString(0, (int)endidx);
}
else firstpartname = partname;
partnum = kit->getNodekitCatalog()->getPartNumber(firstpartname);
if (partnum == SO_CATALOG_NAME_NOT_FOUND) {
if (recsearch) { // search leaf nodekits for this part?
SoBaseKit * orgkit = kit;
assert(path == NULL); // should not do recsearch when creating path
const SoNodekitCatalog * catalog = orgkit->getNodekitCatalog();
for (int i = 1; i < PRIVATE(orgkit)->instancelist.getLength(); i++) {
if (catalog->isLeaf(i) &&
catalog->getType(i).isDerivedFrom(SoBaseKit::getClassTypeId())) {
kit = (SoBaseKit *)PRIVATE(orgkit)->instancelist[i]->getValue();
SbBool didexist = kit != NULL;
if (!didexist) {
orgkit->makePart(i);
kit = (SoBaseKit *)PRIVATE(orgkit)->instancelist[i]->getValue();
}
if (SoBaseKit::findPart(partname, kit, partnum, islist, listidx,
makeifneeded, path, recsearch)) {
return TRUE;
}
else if (!didexist) {
// we created this part, remove it
orgkit->setPart(i, NULL);
}
}
}
kit = orgkit; // return with an error in this kit
}
// nope, not found
return FALSE;
}
assert(partnum < PRIVATE(kit)->instancelist.getLength());
SoSFNode * nodefield = PRIVATE(kit)->instancelist[partnum];
assert(nodefield);
if (makeifneeded && nodefield->getValue() == NULL) {
kit->makePart(partnum);
}
if (path) {
const SoNodekitCatalog * catalog = kit->getNodekitCatalog();
SbList nodestopart;
int parent = catalog->getParentPartNumber(partnum);
while (parent > 0) {
SoNode * node = PRIVATE(kit)->instancelist[parent]->getValue();
if (node == NULL) {
assert(makeifneeded == FALSE);
break;
}
nodestopart.push(node);
parent = catalog->getParentPartNumber(parent);
}
assert(parent == 0 || !makeifneeded);
while (nodestopart.getLength()) {
SoNode * node = nodestopart.pop();
path->append(node);
}
}
if (periodptr == NULL) {
// singlename or singlelistname found, do not recurse any more
return TRUE; // all info has been found, just return TRUE
}
else { // recurse
SoNode * node = nodefield->getValue();
if (node == NULL) return FALSE;
const ptrdiff_t startidx = periodptr - stringptr + 1;
SbString newpartname = partname.getSubString((int)startidx);
if (islist) {
SoNodeKitListPart * list = (SoNodeKitListPart *) node;
int numlistchildren = list->getNumChildren();
if (listidx < 0 || listidx > numlistchildren || (!makeifneeded && listidx == numlistchildren)) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::findPart",
"index %d out of bounds for part ``%s''",
listidx,
firstpartname.getString());
#endif // COIN_DEBUG
return FALSE;
}
else if (listidx == numlistchildren) {
(void) list->createAndAddDefaultChild();
}
SoNode * partnode = list->getChild(listidx);
assert(partnode && partnode->isOfType(SoBaseKit::getClassTypeId()));
kit = (SoBaseKit *)partnode;
if (path) {
path->append(list);
path->append(list->getContainerNode());
}
}
else {
assert(node->isOfType(SoBaseKit::getClassTypeId()));
kit = (SoBaseKit *)node;
}
if (path) path->append(kit);
return SoBaseKit::findPart(newpartname, kit, partnum, islist,
listidx, makeifneeded, path, recsearch);
}
}
//
// makes part, makes sure node is connected in the scene
//
SbBool
SoBaseKit::makePart(const int partnum)
{
assert(partnum > 0 && partnum < PRIVATE(this)->instancelist.getLength());
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
assert(catalog);
assert(PRIVATE(this)->instancelist[partnum]->getValue() == NULL);
SoNode * node = (SoNode *)catalog->getDefaultType(partnum).createInstance();
if (catalog->isList(partnum)) {
SoNodeKitListPart * list = (SoNodeKitListPart *) node;
if (catalog->getListContainerType(partnum) != SoGroup::getClassTypeId()) {
list->setContainerType(catalog->getListContainerType(partnum));
}
const SoTypeList & typelist = catalog->getListItemTypes(partnum);
for (int i = 0; i < typelist.getLength(); i++) {
list->addChildType(typelist[i]);
}
list->lockTypes();
}
return this->setPart(partnum, node);
}
/*!
Sets parts, updates nodekit scene graph, and makes sure graph is
valid with respect to right siblings and parent. This method is
virtual to enable subclasses to detect when a part changes value.
This method is not part of the original SGI Open Inventor API, but
is an extension specific to Coin.
*/
SbBool
SoBaseKit::setPart(const int partnum, SoNode * node)
{
assert(partnum > 0 && partnum < PRIVATE(this)->instancelist.getLength());
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
assert(catalog);
if (node && !node->getTypeId().isDerivedFrom(catalog->getType(partnum))) {
#if COIN_DEBUG
SoDebugError::postWarning("SoBaseKit::setPart",
"Attempted to set part ``%s'' "
"to wrong type. Expected ``%s'', got ``%s''",
catalog->getName(partnum).getString(),
catalog->getType(partnum).getName().getString(),
node->getTypeId().getName().getString());
#endif // COIN_DEBUG
return FALSE;
}
int parentIdx = catalog->getParentPartNumber(partnum);
assert(parentIdx >= 0 && parentIdx < PRIVATE(this)->instancelist.getLength());
SoNode * parent = NULL;
if (parentIdx == 0) parent = this;
else parent = PRIVATE(this)->instancelist[parentIdx]->getValue();
if (parent == NULL) {
this->makePart(parentIdx);
parent = PRIVATE(this)->instancelist[parentIdx]->getValue();
}
assert(parent != NULL);
SoChildList * childlist = parent->getChildren();
assert(childlist != NULL);
// if parent is a node derived from SoGroup, use the SoGroup access
// functions to add/remove/insert children instead of SoChildList
// directly. This is needed for VRML group nodes to work properly
// inside node kits. pederb, 2004-06-23
SoGroup * parentgroup = NULL;
if (parent->isOfType(SoGroup::getClassTypeId())) {
parentgroup = (SoGroup*) parent;
}
SoNode * oldnode = PRIVATE(this)->instancelist[partnum]->getValue();
if (oldnode == node) return TRUE; // part is already inserted
if (childlist->find(node) >= 0) {
// FIXME: should really allow this, but since it's a bit complex
// (we need to somehow keep better track of which SoGroup child
// indices belong to which catalog parts), we just disallow it for
// now. 20020808 mortene.
SoDebugError::postWarning("SoBaseKit::setPart",
"Node pointer (%p) is already used under the same group node in the catalog "
"as a child of %s -- this is not allowed",
node, catalog->getName(parentIdx).getString());
return FALSE;
}
if (oldnode != NULL) { // part exists, replace
int oldIdx = childlist->find(oldnode);
assert(oldIdx >= 0);
if (parentgroup) {
if (node) parentgroup->replaceChild(oldIdx, node);
else parentgroup->removeChild(oldIdx);
}
else {
if (node) childlist->set(oldIdx, node);
else childlist->remove(oldIdx);
}
}
else if (node) { // find where to insert in parent childlist
int rightSibling = this->getRightSiblingIndex(partnum);
if (rightSibling >= 0) { // part has right sibling, insert before
int idx = childlist->find(PRIVATE(this)->instancelist[rightSibling]->getValue());
assert(idx >= 0);
if (parentgroup) {
parentgroup->insertChild(node, idx);
}
else {
childlist->insert(node, idx);
}
}
else {
if (parentgroup) {
parentgroup->addChild(node);
}
else {
childlist->append(node);
}
}
}
// set part field value
PRIVATE(this)->instancelist[partnum]->setValue(node);
return TRUE;
}
//
// returns part number of existing right sibling or -1 if none exists
//
int
SoBaseKit::getRightSiblingIndex(const int partnum)
{
assert(partnum > 0 && partnum < PRIVATE(this)->instancelist.getLength());
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
int sibling = catalog->getRightSiblingPartNumber(partnum);
// iterate until no more siblings or until we find an existing one
while (sibling >= 0 && PRIVATE(this)->instancelist[sibling]->getValue() == NULL) {
sibling = catalog->getRightSiblingPartNumber(sibling);
}
return sibling;
}
//
// Searches the field list to find of a node is in this kit.
// Returns catalog index, -1 if not found
//
// parentnum is checked if >= 0
//
int
SoBaseKit::findNodeInThisKit(SoNode * node, const int parentnum) const
{
const SoNodekitCatalog * catalog = this->getNodekitCatalog();
if (node == (SoNode *)this) return 0;
int n = PRIVATE(this)->instancelist.getLength();
for (int i = 1; i < n; i++) {
if (PRIVATE(this)->instancelist[i]->getValue() == node &&
(parentnum < 0 || catalog->getParentPartNumber(i) == parentnum))
return i;
}
return -1;
}
// ******* methods in SoBaseKitP are below ******************************
//
// copy the fields in kit into a new fielddata. This is done to get
// the correct write order: non-part fields first, then leaf parts,
// then non-leaf parts.
//
void
SoBaseKitP::createWriteData(void)
{
this->writedata = new SoFieldData;
const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
const SoFieldData * fielddata = kit->getFieldData();
int n = fielddata->getNumFields();
for (int pass = 0; pass < 3; pass++) {
for (int i = 0; i < n; i++) {
int part = catalog->getPartNumber(fielddata->getFieldName(i));
// NB: earlier (before 2003-03-26) we did not write private
// parts. However, several users have reported that SGI/TGS
// Inventor do this so we have to write them too.
// pederb, 2003-03-26
if ((pass == 0 && part < 0) ||
(pass == 1 && part > 0 && catalog->isLeaf(part)) ||
(pass == 2 && part > 0 && !catalog->isLeaf(part))) {
this->writedata->addField(this->kit,
fielddata->getFieldName(i).getString(),
fielddata->getField(this->kit, i));
}
}
}
}
//
// test if parent part of a part is going to write, and if so
// write part even if isDefault()
//
void
SoBaseKitP::testParentWrite(void)
{
const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
int n = this->instancelist.getLength();
for (int i = 1; i < n; i++) {
SoSFNode * field = this->instancelist[i];
if (field->isDefault()) { // we might not write
SoNode * node = field->getValue();
// don't write if NULL, of course
if (node) {
int parent = catalog->getParentPartNumber(i);
if (parent > 0) {
assert(this->writedata);
SbName dummy;
SoNode * parentnode = this->instancelist[parent]->getValue();
// we must write if parent is going to write
if (parentnode &&
!this->instancelist[parent]->isDefault()) {
field->setDefault(FALSE);
}
}
}
}
}
}
// Copy parts into 'partlist'. All parts have already been copied, but
// we need to update the parts that have a parent as a part, since the
// part node has already been copied by the parent, and we need to use
// that child node pointer, not the copied part.
void
SoBaseKitP::copyParts(const SoBaseKit * srckit, SbList & partlist,
const SbBool copyconnections)
{
int i;
const int n = this->instancelist.getLength();
const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
// convenient reference
const SbList & srcfields = srckit->getCatalogInstances();
// copy parts that do not have a parent as a part
for (i = 1; i < n; i++) {
SoNode * dstnode = this->instancelist[i]->getValue();
if (dstnode && catalog->getParentPartNumber(i) == 0) {
SoNode * srcnode = srcfields[i]->getValue();
assert(dstnode != srcnode);
assert(srcnode != NULL);
assert(srcnode->getTypeId() == dstnode->getTypeId());
srcnode->assertAlive();
dstnode->assertAlive();
// the node has been copied since we called
// SoNode::copyContents() . We just need to store the pointer
dstnode->ref(); // ref before inserting into list
if (partlist[i]) partlist[i]->unref();
partlist[i] = dstnode;
}
}
// copy parts where parent is a part. These parts will already
// have been copied, but we need to figure out the parent part node,
// and use the correct child node as the part node instead of the
// already copied part node.
for (i = 1; i < n; i++) {
int parent = catalog->getParentPartNumber(i);
if (parent > 0 && this->instancelist[i]->getValue()) {
SoNode * srcgroup = srcfields[parent]->getValue();
assert(srcgroup);
SoNode * dstgroup = partlist[parent];
assert(dstgroup);
assert(dstgroup->getChildren());
assert(srcgroup->getChildren());
// find child index in src kit
int childidx = srcgroup->getChildren()->find(srcfields[i]->getValue());
assert(childidx >= 0);
// use the already copied child as part node
assert(childidx < dstgroup->getChildren()->getLength());
SoNode * child = (*(dstgroup->getChildren()))[childidx];
child->ref(); // ref before inserting
if (partlist[i]) partlist[i]->unref(); // unref old node in list
partlist[i] = child;
}
}
}
void
SoBaseKitP::setParts(SbList partlist, const SbBool leafparts)
{
const int n = this->instancelist.getLength();
const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
for (int i = 1; i < n; i++) {
SoNode * node = partlist[i];
if (node) {
node->ref();
SbBool leaftst = catalog->isLeaf(i);
if (leaftst == leafparts) { // correct pass ?
if (!leaftst) {
// if it's not a leaf, remove children as the correct children
// will be added when children parts are set.
assert(node->getChildren());
node->getChildren()->truncate(0);
}
this->kit->setPart(i, node);
}
}
}
}
//
// Adds a SoNodekitDetail to the picked point. path should
// contain this kit.
//
void
SoBaseKitP::addKitDetail(SoFullPath * path, SoPickedPoint * pp)
{
const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
assert(path->findNode(this->kit) >= 0);
const int n = path->getLength();
for (int i = path->findNode(this->kit) + 1; i < n; i++) {
SoNode * node = path->getNode(i);
int idx = this->kit->findNodeInThisKit(node, -1);
if (idx > 0 && catalog->isLeaf(idx)) {
SoNodeKitDetail * detail = new SoNodeKitDetail;
detail->setNodeKit(this->kit);
detail->setPart(node);
SbString partname(catalog->getName(idx));
// check if node is a SoNodeKitListPart, and if the
// path extends into the children. Supply index in partname
// if this is the case.
if (node->isOfType(SoNodeKitListPart::getClassTypeId()) &&
path->getLength() >= i + 2) {
SbString str;
str.sprintf("%s[%d]",
partname.getString(),
path->getIndex(i+2));
partname = SbName(str.getString());
}
detail->setPartName(partname);
pp->setDetail(detail, this->kit);
// finished
break;
}
}
}
// Reading in parts of nested nodekits does not allow certain shortcuts
// that are specified by the Inventor Mentor. The Mentor specifies that
// within nested nodekits intermediary kits can be left out and will be
// created automatically. Reported by Gerhard Reitmayr.
SbBool
SoBaseKitP::readUnknownFields(SoInput *in, SoFieldData *&unknownfielddata)
{
const SoFieldData * fd = PUBLIC(this)->getFieldData();
// Binary format
if (in->isBinary()) {
SbBool notbuiltin;
return fd->read(in, PUBLIC(this), TRUE, notbuiltin);
}
SbBool firstfield = TRUE;
SbName fielddescriptionsmarker("fields");
// ASCII format
// keep reading fields until we hit close bracket
while (TRUE) {
// read first character - if none, EOF
char c;
if (!in->read(c))
return FALSE;
in->putBack(c);
if (c == '}')
return TRUE;
// read fieldname with no identifier, to be able to read names like
// appearance.material
SbName fieldname;
if (!in->read(fieldname, FALSE))
return TRUE;
// if this is the first field we try to read, it might be the
// field descriptions for extension node kits. Detect and read.
if (firstfield) {
firstfield = FALSE;
if (fieldname == fielddescriptionsmarker) {
if (!fd->readFieldDescriptions(in, PUBLIC(this), 0, FALSE)) {
return FALSE;
}
continue; // read next field
}
}
// try to read data into one of the fields in this nodekit first.
// SoFieldData::read() will return TRUE and set foundname to FALSE
// if the field isn't part of the node(kit)
SbBool foundname;
if (!fd->read(in, PUBLIC(this), fieldname, foundname))
return FALSE;
if (!foundname) {
// add a node pointer field with this name to the unknownFieldData,
// and read it
unknownfielddata->addField(PUBLIC(this), fieldname.getString(),
new SoSFNode);
if (!unknownfielddata->read(in, PUBLIC(this), fieldname, foundname))
return FALSE;
}
}
// Will never be reached, but functions with a return value other than
// void must return *something* by default. At least gcc-4.0.0
// (Apple snapshot 20041026, default in Mac OS 10.4) will warn.
return TRUE;
}