1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36 
37 #ifdef HAVE_NODEKITS
38 
39 /*!
40   \class SoBaseKit SoBaseKit.h Inventor/nodekits/SoBaseKit.h
41   \brief The SoBaseKit class is the toplevel superclass for nodekits.
42 
43   \ingroup nodekits
44 
45   Node kits are collections of nodes and other node kits (from here on
46   node kits which are part of some other node kit, will only be referred
47   to as nodes or parts, see catalogs and parts), organized in a way
48   that is convenient for its use. A node kit inherits SoNode and can
49   thus be inserted into a scenegraph as any other node.
50 
51   The organizing of the nodes and node kits of some node kit, is done
52   through catalogs. A node kit's catalog describes the nodes that can
53   be members of the node kit. These members are called parts. Thus a
54   node kit has a catalog describing the parts that it offers to the
55   user.
56 
57   Each part in the catalog has some values saying something about the
58   part itself and about the role the part plays in the scenegraph.
59   Those values are:
60 
61   <dl>
62   <dt> Name
63   <dd> The name of the part.
64   <dt> Type
65   <dd> The part's node type.
66   <dt> Default Type
67   <dd> If the part's type is an abstract superclass, this value will hold
68   the default subclass used by this part.
69   <dt> Created by Default?
70   <dd> Holds \c TRUE if the part should be instantiated when the node kit
71   is instantiated, otherwise the part is kept empty until it is set by some
72   of the means applicable.
73   <dt> Parent Name
74   <dd> The name of the part that is this part's parent.
75   <dt> Right Sibling
76   <dd> The name of the part that is the part immediately to the right of
77   this part in the node kit scenegraph.
78   <dt> Is it a List?
79   <dd> Holds \c TRUE if the part is a list, otherwise it is \c FALSE. See
80   SoNodeKitListPart for more info on node kit lists.
81   <dt> List Cointainer Type
82   <dd> The type of group node used to hold the items if the part is a list.
83   <dt> List Element Type
84   <dd> The types of nodes that is allowed to be held by this part if the part
85   is a list.
86   <dt> Is It Public?
87   <dd> Holds \c TRUE if the part should be publically available, otherwise
88   it holds \c FALSE.
89   </dl>
90 
91   Node kits use lazy instantiation when it creates it's parts. This means
92   that the nodes making up the parts of the nodekit only are created when
93   they are needed. If the "Created by Default?" holds TRUE, then the part
94   is created when the node kit itself is instantiated. If not, parts are
95   created when they are requested through SoBaseKit::getPart() or the
96   SO_GET_PART() macro, or created with SoBaseKit::set(). Also, if a part is
97   set with SoBaseKit::setPart() or the SO_SET_PART() macro, any previously
98   uncreated parts above the set part in the hierarchy, is created
99   automatically.
100 
101   The advantages of using node kits to represent a scenegraph are many.
102   \li Since a node kit collects nodes into a single unit, it becomes
103       an extra abstraction layer for the application programmer. Such
104       a layer can represent a model of a human being as one unit where
105       subunits as arms, legs, textures, etc are contained within. Thus
106       we can instantiate a model of a human by creating an instance of
107       the node kit, instead of having to create a possibly large
108       amount of nodes needed for such a model.
109   \li A part of the node kit doesn't have one specific setup. A shape part
110       can e.g. be swapped with any other shape, since they are of the same
111       type. If the node kit of a human has a part called "head" which is of
112       type SoShape, it might default to a sphere. But if the programmer
113       thinks that a cube might fit better, one can set the "head" part to
114       a cube instead, or maybe a face set representing a complex model of
115       a head.
116   \li Node kits can have as simple or as complex catalogs as needed. The
117       nodes included in the node kit can, if needed, represent the
118       whole range of Inventor features. One part can as easily be of a
119       node kit type, making it possible to create hierarchies of node kits.
120       Having a node kit of a human, it might be feasible to have sub node
121       kits describing the different body parts.
122   \li Node kits are an efficient way of creating scenegraphs. If some
123       part of it isn't needed at the moment of node kit instantiation,
124       they aren't created. Thus parts are only created when needed, either
125       by the application or some other part.
126   \li The application code becomes smaller and easier to read, as the node
127       kits provides simple routines for creating and setting parts.
128   \li New node kits can be created through subclassing to obtain simple
129       setups of scenegraphs best fitted to the application.
130 
131   The usage of a node kit is straightforward. Below follows a code
132   example showing some simple SoShapeKit usage.
133 
134   \code
135 
136   #include <Inventor/Qt/SoQt.h>
137   #include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
138   #include <Inventor/nodekits/SoShapeKit.h>
139   #include <Inventor/nodes/SoSeparator.h>
140   #include <Inventor/nodes/SoCube.h>
141 
142   int
143   main(int argc, char ** argv)
144   {
145     QWidget * window = SoQt::init(argv[0]);
146 
147     SoQtExaminerViewer * viewer = new SoQtExaminerViewer(window);
148 
149     // Instantiating a shape kit, by default creating a simple sphere.
150     SoShapeKit * shapekit = new SoShapeKit;
151     // Swapping the sphere with a cube.
152     shapekit->setPart("shape", new SoCube);
153     // Setting the cube to be rendered in the color red. The shape kit
154     // has a SoAppearanceKit as one of it's parts. The "material" part
155     // used to set the color of the shape, really belongs the
156     // appearance kit. If the SoShapeKit::set() is used, it will
157     // check if some of its sub kits has a part with the name given,
158     // and delegate the setting to the correct kit.
159     shapekit->set("material", "diffuseColor 1 0 0");
160 
161     SoSeparator * newroot = new SoSeparator;
162     newroot->ref();
163 
164     newroot->addChild(shapekit);
165 
166     viewer->setSceneGraph(newroot);
167 
168     viewer->show();
169     SoQt::show(window);
170 
171     SoQt::mainLoop();
172     delete viewer;
173 
174     newroot->unref();
175     return 0;
176   }
177   \endcode
178 
179   The above code snippet will produce a viewer with a side view to
180   the scene shown below:
181 
182   <center>
183   \image html basekitexample.png "Rendering of Example Scenegraph"
184   </center>
185 
186   Notice that the code needed for creating this simple shape using
187   a shape kit, amounts to this:
188 
189   \code
190    SoShapeKit * shapekit = new SoShapeKit;
191 
192    shapekit->setPart("shape", new SoCube);
193    shapekit->set("material", "diffuseColor 1 0 0");
194   \endcode
195 
196   ..while doing it without shape kits amounts to this:
197 
198   \code
199   SoSeparator * root = new SoSeparator;
200   SoMaterial * material = new SoMaterial;
201   material->diffuseColor.setValue(1,0,0);
202   root->addChild(material);
203   root->addChild(new SoCube);
204   \endcode
205 
206   ..so even for this miniscule mock-up example, you save on code
207   verbosity and complexity.
208 
209 
210   \TOOLMAKER_REF
211 
212 
213   Following is a complete example of a node kit extension. The node
214   kit is a kit which automatically scales a shape so it will be the
215   same size in screen-pixels, no matter which distance it is from the
216   camera. This is useful for marker graphics. The shape defaults to a
217   cube, but can be set by the programmer to any shape or scene
218   sub-graph.
219 
220   The header file:
221 
222   \code
223   // Copyright (C) Kongsberg Oil & Gas Technologies. All rights reserved.
224 
225   #ifndef COIN_SHAPESCALE_H
226   #define COIN_SHAPESCALE_H
227   #include <Inventor/nodekits/SoSubKit.h>
228   #include <Inventor/nodekits/SoBaseKit.h>
229   #include <Inventor/fields/SoSFFloat.h>
230 
231   class SbViewport;
232   class SoState;
233   class SbColor;
234   class SbVec2s;
235 
236   class ShapeScale : public SoBaseKit {
237     typedef SoBaseKit inherited;
238 
239     SO_KIT_HEADER(ShapeScale);
240 
241     SO_KIT_CATALOG_ENTRY_HEADER(topSeparator);
242     SO_KIT_CATALOG_ENTRY_HEADER(scale);
243     SO_KIT_CATALOG_ENTRY_HEADER(shape);
244 
245    public:
246     ShapeScale(void);
247     static void initClass(void);
248 
249     SoSFFloat active;
250     SoSFFloat projectedSize;
251 
252    protected:
253     virtual void GLRender(SoGLRenderAction * action);
254     virtual ~ShapeScale();
255   };
256 
257   #endif // ! SHAPESCALE_H
258   \endcode
259 
260   The source code for the example:
261 
262   \code
263   // Copyright (C) Kongsberg Oil & Gas Technologies. All rights reserved.
264 
265   //  The ShapeScale class is used for scaling a shape based on
266   //  projected size.
267   //
268   //  This nodekit can be inserted in your scene graph to add for
269   //  instance 3D markers that will be of a constant projected size.
270   //
271   //  The marker shape is stored in the "shape" part. Any kind of node
272   //  can be used, even group nodes with several shapes, but the
273   //  marker shape should be approximately of unit size, and with a
274   //  center position in (0, 0, 0).
275 
276 
277   //  SoSFFloat ShapeScale::active
278   //  Turns the scaling on/off. Default value is TRUE.
279 
280 
281   //  SoSFFloat ShapeScale::projectedSize
282   //  The requested projected size of the shape. Default value is 5.0.
283 
284   #include "ShapeScale.h"
285 
286   #include <Inventor/actions/SoGLRenderAction.h>
287   #include <Inventor/nodes/SoShape.h>
288   #include <Inventor/nodes/SoScale.h>
289   #include <Inventor/nodes/SoCube.h>
290   #include <Inventor/nodes/SoSeparator.h>
291   #include <Inventor/elements/SoViewVolumeElement.h>
292   #include <Inventor/elements/SoViewportRegionElement.h>
293   #include <Inventor/elements/SoModelMatrixElement.h>
294 
295   SO_KIT_SOURCE(ShapeScale);
296 
297 
298   //  Constructor.
299   ShapeScale::ShapeScale(void)
300   {
301     SO_KIT_CONSTRUCTOR(ShapeScale);
302 
303     SO_KIT_ADD_FIELD(active, (TRUE));
304     SO_KIT_ADD_FIELD(projectedSize, (5.0f));
305 
306     SO_KIT_ADD_CATALOG_ENTRY(topSeparator, SoSeparator, FALSE, this, \x0, FALSE);
307     SO_KIT_ADD_CATALOG_ABSTRACT_ENTRY(shape, SoNode, SoCube, TRUE, topSeparator, \x0, TRUE);
308     SO_KIT_ADD_CATALOG_ENTRY(scale, SoScale, FALSE, topSeparator, shape, FALSE);
309 
310     SO_KIT_INIT_INSTANCE();
311   }
312 
313 
314   // Destructor.
315   ShapeScale::~ShapeScale()
316   {
317   }
318 
319   // Initializes this class. Call before using it.
320 
321   void
322   ShapeScale::initClass(void)
323   {
324     SO_KIT_INIT_CLASS(ShapeScale, SoBaseKit, "BaseKit");
325   }
326 
327   static void
328   update_scale(SoScale * scale, const SbVec3f & v)
329   {
330     // only write to field when scaling has changed.
331     if (scale->scaleFactor.getValue() != v) {
332       scale->scaleFactor = v;
333     }
334   }
335 
336   // Overridden to (re)initialize scaling before rendering marker.
337   void
338   ShapeScale::GLRender(SoGLRenderAction * action)
339   {
340     SoState * state = action->getState();
341 
342     SoScale * scale = (SoScale*) this->getAnyPart(SbName("scale"), TRUE);
343     if (!this->active.getValue()) {
344       update_scale(scale, SbVec3f(1.0f, 1.0f, 1.0f));
345     }
346     else {
347       const SbViewportRegion & vp = SoViewportRegionElement::get(state);
348       const SbViewVolume & vv = SoViewVolumeElement::get(state);
349       SbVec3f center(0.0f, 0.0f, 0.0f);
350       float nsize = this->projectedSize.getValue() / float(vp.getViewportSizePixels()[1]);
351       SoModelMatrixElement::get(state).multVecMatrix(center, center); // transform to WCS
352       float scalefactor = vv.getWorldToScreenScale(center, nsize);
353       update_scale(scale, SbVec3f(scalefactor, scalefactor, scalefactor));
354     }
355     inherited::GLRender(action);
356   }
357   \endcode
358 
359   And a complete example showing how one can use this node kit:
360 
361   \code
362   // Copyright (C) Kongsberg Oil & Gas Technologies. All rights reserved.
363 
364   #include <Inventor/Qt/SoQt.h>
365   #include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
366   #include <Inventor/SoInput.h>
367   #include <Inventor/SoOutput.h>
368   #include <Inventor/SoPickedPoint.h>
369   #include <Inventor/actions/SoRayPickAction.h>
370   #include <Inventor/events/SoMouseButtonEvent.h>
371   #include <Inventor/nodes/SoBaseColor.h>
372   #include <Inventor/nodes/SoCube.h>
373   #include <Inventor/nodes/SoEventCallback.h>
374   #include <Inventor/nodes/SoSeparator.h>
375   #include <Inventor/nodes/SoSwitch.h>
376   #include <Inventor/nodes/SoTranslation.h>
377   #include <cassert>
378   #include <cstdlib>
379   #include <ctime>
380 
381   #include "ShapeScale.h"
382 
383   // Returns random value between 0.0f and 1.0f.
384   static float
385   normalized_rand(void)
386   {
387     return float(rand())/float(RAND_MAX);
388   }
389 
390   static SoSeparator *
391   construct_new_marker(const SbVec3f & v)
392   {
393     SoSeparator * markerroot = new SoSeparator;
394 
395     SoTranslation * t = new SoTranslation;
396     t->translation = v;
397     markerroot->addChild(t);
398 
399     ShapeScale * kit = new ShapeScale;
400     kit->active = TRUE;
401     kit->projectedSize = 5.0f;
402 
403     // create the marker
404     SoSeparator * markersep = new SoSeparator;
405 
406     SoBaseColor * mat = new SoBaseColor;
407     mat->rgb.setValue(normalized_rand(), normalized_rand(), normalized_rand());
408     markersep->addChild(mat);
409 
410     // marker shape should be unit size, with center in (0.0f, 0.0f, 0.0f)
411     SoCube * cube = new SoCube;
412     cube->width = 1.0f;
413     cube->height = 1.0f;
414     cube->depth = 1.0f;
415 
416     markersep->addChild(cube);
417     kit->setPart("shape", markersep);
418     markerroot->addChild(kit);
419 
420     return markerroot;
421   }
422 
423   static void
424   event_cb(void * ud, SoEventCallback * n)
425   {
426     const SoMouseButtonEvent * mbe = (SoMouseButtonEvent *)n->getEvent();
427 
428     if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 &&
429       mbe->getState() == SoButtonEvent::DOWN) {
430 
431       SoQtExaminerViewer * viewer = (SoQtExaminerViewer *)ud;
432 
433       SoRayPickAction rp(viewer->getViewportRegion());
434       rp.setPoint(mbe->getPosition());
435       rp.apply(viewer->getSceneManager()->getSceneGraph());
436 
437       SoPickedPoint * point = rp.getPickedPoint();
438       if (point == NULL) {
439         (void)fprintf(stderr, "\n** miss! **\n\n");
440         return;
441       }
442 
443       n->setHandled();
444 
445       const SoPath * p = rp.getCurPath();
446 
447       for (int i = 0; i < p->getLength(); i++) {
448         SoNode * n = p->getNodeFromTail(i);
449         if (n->isOfType(SoGroup::getClassTypeId())) {
450           SoGroup * g = (SoGroup *)n;
451           g->addChild(construct_new_marker(point->getPoint()));
452           break;
453         }
454       }
455     }
456   }
457 
458   void
459   show_instructions(void)
460   {
461     (void)fprintf(stdout,
462       "\nThis example program demonstrates the use of the ShapeScale nodekit.\n"
463       "\nQuick instructions:\n\n"
464       "  * place the marker by clicking on a shape with the left mouse button\n"
465       "  * hit ESC to toggle back and forth to view mode\n"
466       "  * zoom back and forth to see how the markers stay the same size\n\n");
467   }
468 
469   int
470   main(int argc, char ** argv)
471   {
472     if (argc != 2) {
473       (void) fprintf(stderr,"\nSpecify an Inventor file as argument.\n");
474       return -1;
475     }
476 
477     QWidget * window = SoQt::init(argv[0]);
478     ShapeScale::initClass(); // init our extension nodekit
479 
480     SoQtExaminerViewer * ex1 = new SoQtExaminerViewer(window);
481 
482     SoInput input;
483     SbBool ok = input.openFile(argv[1]);
484     if (!ok) {
485       (void) fprintf(stderr, "Unable to open file: %s\n", argv[1]);
486       return -1;
487     }
488 
489     SoSeparator * root = SoDB::readAll(&input);
490 
491     if (root == NULL) {
492       (void) fprintf(stderr, "Unable to read file: %s\n", argv[1]);
493       return -1;
494     }
495 
496     show_instructions();
497 
498     SoSeparator * newroot = new SoSeparator;
499     newroot->ref();
500 
501     newroot->addChild(root);
502 
503     // create event callback and marker nodes
504     SoSeparator * sep = new SoSeparator;
505     newroot->addChild(sep);
506 
507     SoEventCallback * ecb = new SoEventCallback;
508     ecb->addEventCallback(SoMouseButtonEvent::getClassTypeId(), event_cb, ex1);
509     sep->addChild(ecb);
510 
511     ex1->setSceneGraph(newroot);
512     ex1->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_BLEND);
513     ex1->setViewing(FALSE);
514 
515     ex1->show();
516     SoQt::show(window);
517 
518     SoQt::mainLoop();
519     delete ex1;
520 
521     newroot->unref();
522     return 0;
523   }
524   \endcode
525 */
526 
527 #include <Inventor/nodekits/SoBaseKit.h>
528 
529 #include <cstdlib>
530 #include <climits>
531 #include <cctype>
532 #include <cstring>
533 
534 #include <Inventor/nodekits/SoNodeKitListPart.h>
535 #include <Inventor/nodes/SoSeparator.h>
536 #include <Inventor/nodes/SoCallback.h>
537 #include <Inventor/nodes/SoEventCallback.h>
538 #include <Inventor/misc/SoChildList.h>
539 #include <Inventor/actions/SoSearchAction.h>
540 #include <Inventor/actions/SoAudioRenderAction.h>
541 #include <Inventor/actions/SoGetMatrixAction.h>
542 #include <Inventor/actions/SoRayPickAction.h>
543 #include <Inventor/actions/SoGetBoundingBoxAction.h>
544 #include <Inventor/actions/SoWriteAction.h>
545 #include <Inventor/SoInput.h>
546 #include <Inventor/SoOutput.h>
547 #include <Inventor/details/SoNodeKitDetail.h>
548 #include <Inventor/SoPickedPoint.h>
549 #include <Inventor/lists/SoPickedPointList.h>
550 #include <Inventor/lists/SoNodeList.h>
551 #include <Inventor/errors/SoReadError.h>
552 #include <Inventor/C/tidbits.h> // coin_isspace()
553 #include <Inventor/errors/SoDebugError.h>
554 
555 #include "coindefs.h" // COIN_OBSOLETED()
556 #include "io/SoWriterefCounter.h"
557 #include "nodekits/SoSubKitP.h"
558 
559 class SoBaseKitP {
560 public:
SoBaseKitP(SoBaseKit * kit)561   SoBaseKitP(SoBaseKit * kit) : kit(kit) { }
562 
563   SoBaseKit * kit;
564   SoFieldData * writedata;
565   SbBool didcount;
566 
567   // This array is a 1-1 mapping of the fields corresponding to the
568   // catalog parts. Catalog indices will therefore also be used as
569   // indices into this array.
570   SbList<SoSFNode*> instancelist;
571 
572   void addKitDetail(SoFullPath * path, SoPickedPoint * pp);
573   void createWriteData(void);
574   void testParentWrite(void);
575 
576   void copyParts(const SoBaseKit * srckit, SbList <SoNode*> & partlist,
577                  const SbBool copyconnections);
578 
579   void setParts(SbList <SoNode*> partlist, const SbBool leafparts);
580 
581   SbBool readUnknownFields(SoInput *in, SoFieldData *&unknownFieldData );
582 };
583 
584 #define PRIVATE(p) ((p)->pimpl)
585 #define PUBLIC(p) ((p)->kit)
586 
587 SbBool SoBaseKit::searchchildren = FALSE;
588 
589 SO_KIT_SOURCE(SoBaseKit);
590 
591 /*!
592   \fn const SoNodekitCatalog * SoBaseKit::getClassNodekitCatalog(void)
593   Returns the nodekit catalog which defines the layout of this
594   class' kit.
595 */
596 
597 /*!
598   \fn const SoNodekitCatalog * SoBaseKit::getNodekitCatalog(void) const
599   Returns the nodekit catalog which defines the layout of this
600   class' kit.
601 */
602 
603 /*!
604   \fn const SoNodekitCatalog ** SoBaseKit::getClassNodekitCatalogPtr(void)
605   Returns the pointer to the pointer of the nodekit catalog
606   for this class.
607 */
608 
609 
610 /*!
611   \var SoChildList * SoBaseKit::children
612   \COININTERNAL
613 */
614 /*!
615   \var SbBool SoBaseKit::connectionsSetUp
616   \COININTERNAL
617 */
618 
619 
620 /*!
621   Constructor.
622 
623   This is the top-level superclass of all node kit and dragger
624   classes. The catalog structure of SoBaseKit is as follows:
625 
626   \verbatim
627   CLASS SoBaseKit
628   -->"this"
629   -->   "callbackList"
630   \endverbatim
631 
632   \NODEKIT_POST_DIAGRAM
633 
634   \NODEKIT_PRE_TABLE
635 
636   \verbatim
637   CLASS SoBaseKit
638   PVT   "this",  SoBaseKit  ---
639         "callbackList",  SoNodeKitListPart [ SoCallback, SoEventCallback ]
640   \endverbatim
641 
642   \NODEKIT_POST_TABLE
643 
644   As can be seen from the catalog, all node kits can have a callback
645   node in front of all other nodes in the kit. This is handy for
646   catching events that should go to application processing.
647 */
SoBaseKit(void)648 SoBaseKit::SoBaseKit(void)
649 {
650   PRIVATE(this) = new SoBaseKitP(this);
651   PRIVATE(this)->writedata = NULL;
652 
653   SO_KIT_INTERNAL_CONSTRUCTOR(SoBaseKit);
654 
655   // Can't use ADD_CATALOG_ENTRY macro for the toplevel "this" entry,
656   // as we don't want to call SO_NODE_ADD_FIELD(). This is how the
657   // invocation would have looked if we could use the macro:
658   //
659   // SO_KIT_ADD_CATALOG_ENTRY(this, SoBaseKit, TRUE, "", "", FALSE);
660 
661   SoBaseKit::classcatalog->addEntry("this",
662                                     SoBaseKit::getClassTypeId(),
663                                     SoBaseKit::getClassTypeId(),
664                                     TRUE,
665                                     "",
666                                     "",
667                                     FALSE,
668                                     SoType::badType(),
669                                     SoType::badType(),
670                                     FALSE);
671 
672   SO_KIT_ADD_CATALOG_LIST_ENTRY(callbackList, SoSeparator, TRUE, this, "", SoCallback, TRUE);
673   SO_KIT_ADD_LIST_ITEM_TYPE(callbackList, SoEventCallback);
674 
675   // this could be created on demand, but will make it more complicated
676   this->children = new SoChildList(this);
677 
678   this->connectionsSetUp = FALSE;
679   SO_KIT_INIT_INSTANCE();
680 }
681 
682 /*!
683   Destructor.
684 */
~SoBaseKit()685 SoBaseKit::~SoBaseKit()
686 {
687   delete this->children;
688   delete PRIVATE(this);
689 }
690 
691 // Doc in superclass
692 void
initClass(void)693 SoBaseKit::initClass(void)
694 {
695   SO_NODE_INTERNAL_INIT_CLASS(SoBaseKit, SO_FROM_INVENTOR_1);
696   // set rayPick method
697   SoType type = SoBaseKit::getClassTypeId();
698   SoRayPickAction::addMethod(type, SoNode::rayPickS);
699   SoAudioRenderAction::addMethod(type,
700                                  SoAudioRenderAction::callDoAction);
701   SoBaseKit::searchchildren = FALSE;
702 }
703 
704 /*!
705   Returns a pointer to the node part with \a partname.
706 
707   This method calls SoBaseKit::getAnyPart() with \a leafcheck and \a
708   publiccheck both set to \c TRUE.
709 
710   See the documentation of SoBaseKit::getAnyPart() for information on
711   how to use \a partname and \a makeifneeded, and what you can expect
712   to get returned from this method.
713 */
714 SoNode *
getPart(const SbName & partname,SbBool makeifneeded)715 SoBaseKit::getPart(const SbName & partname, SbBool makeifneeded)
716 {
717   return this->getAnyPart(partname, makeifneeded, TRUE, TRUE);
718 }
719 
720 /*!
721   Returns the full path name to a catalog part, given the part's
722   current item pointer.
723 */
724 SbString
getPartString(const SoBase * part)725 SoBaseKit::getPartString(const SoBase * part)
726 {
727   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
728   if (part->isOfType(SoNode::getClassTypeId())) {
729     int idx = this->findNodeInThisKit((SoNode *)part);
730     if (idx >= 0) {
731       return SbString(catalog->getName(idx).getString());
732     }
733     return SbString();
734   }
735   else if (part->isOfType(SoPath::getClassTypeId())) {
736     SoFullPath * path = (SoFullPath *)part;
737     int pathidx = path->findNode(this);
738     if (pathidx < 0) return SbString();
739     SoBaseKit * kit = this;
740     SbString partname;
741     int parentnum = 0;
742     SoNode * tail = path->getTail();
743     SoNode * node = kit;
744     while (node != tail) {
745       node = path->getNode(++pathidx);
746       int partnum = kit->findNodeInThisKit(node, parentnum);
747       if (partnum < 0) {
748 #if COIN_DEBUG
749         SoDebugError::postWarning("SoBaseKit::getPartString",
750                                   "Illegal path");
751 #endif // COIN_DEBUG
752         return SbString();
753       }
754       if (catalog->isLeaf(partnum)) {
755         if (partname != "") partname += '.';
756         partname += catalog->getName(partnum).getString();
757       }
758       if (node->isOfType(SoNodeKitListPart::getClassTypeId())) {
759         // no sense in using SoNodeKitListPart as a non-leaf node, right?
760         assert(catalog->isLeaf(partnum));
761         SoNodeKitListPart * list = (SoNodeKitListPart *)node;
762         pathidx += 2; // // skip container node
763         if (pathidx >= path->getLength()) {
764 #if COIN_DEBUG
765           SoDebugError::postWarning("SoBaseKit::getPartString",
766                                     "Path too short");
767 #endif // COIN_DEBUG
768           return SbString();
769         }
770         node = path->getNode(pathidx);
771         int childidx = list->findChild(node);
772         assert(childidx >= 0);
773         partname += '[';
774         partname.addIntString(childidx);
775         partname += ']';
776       }
777       if (node->isOfType(SoBaseKit::getClassTypeId())) {
778         kit = (SoBaseKit *) node;
779         catalog = kit->getNodekitCatalog();
780         parentnum = 0;
781       }
782       else {
783         // search more in this kit
784         parentnum = partnum;
785       }
786     }
787     return partname;
788   }
789   return SbString();
790 }
791 
792 /*!
793   Calls SoBaseKit::createPathToAnyPart() with \a leafcheck \c TRUE,
794   and \a publiccheck \c TRUE (and other arguments as given to this
795   function).
796 
797   See SoBaseKit::createPathToAnyPart() for documentation.
798 */
799 SoNodeKitPath *
createPathToPart(const SbName & partname,SbBool makeifneeded,const SoPath * pathtoextend)800 SoBaseKit::createPathToPart(const SbName & partname, SbBool makeifneeded, const SoPath * pathtoextend)
801 {
802   return this->createPathToAnyPart(partname, makeifneeded, TRUE, TRUE, pathtoextend);
803 }
804 
805 /*!
806   Sets the catalog part given by \a partname to the \a from node pointer.
807 */
808 SbBool
setPart(const SbName & partname,SoNode * from)809 SoBaseKit::setPart(const SbName & partname, SoNode * from)
810 {
811   return this->setAnyPart(partname, from, FALSE);
812 }
813 
814 static const char *
skip_spaces(const char * ptr)815 skip_spaces(const char * ptr)
816 {
817   // ANSI C isspace() takes the current locale into account. Under
818   // MSWindows, this can lead to "interesting" artifacts, like a case
819   // with RR tracked down and fixed by <thammer@sim.no> where a
820   // character (was it �?) with ASCII value > 127 made isspace()
821   // return non-nil on a German system. So we're using our own
822   // locale-independent isspace() implementation instead.
823   while (coin_isspace(*ptr)) ptr++;
824   return ptr;
825 }
826 
827 static int
find_partname_length(const char * ptr)828 find_partname_length(const char * ptr)
829 {
830   int cnt = 0;
831   while (ptr[cnt] && !coin_isspace(ptr[cnt]) &&
832          ptr[cnt] != '{' && ptr[cnt] != '}') {
833     cnt++;
834   }
835   return cnt;
836 }
837 
838 /*!
839   Sets nodekit part field values. The input argument string is of the
840   format:
841 
842   \code
843   partname {
844     fieldname fieldval
845     fieldname fieldval
846     [...]
847   }
848   partname {
849     fieldname fieldval
850     fieldname fieldval
851     [...]
852   }
853   [...]
854   \endcode
855 
856   (Whitespace layout is ignored, as always for Inventor format input
857   strings.)
858 
859   Here's an example, changing several values of the camera part of an
860   SoCameraKit instance:
861 
862   \code
863   kit->set("camera { heightAngle 0.3927  nearDistance 1.1  farDistance 999.9 }");
864   \endcode
865 */
866 SbBool
set(const char * namevaluepairliststring)867 SoBaseKit::set(const char * namevaluepairliststring)
868 {
869   const size_t stringlen = strlen(namevaluepairliststring); // cache this value
870   const char * currptr = skip_spaces(namevaluepairliststring);
871   SoInput memInput;
872 
873   while (*currptr) {
874     int partnamelen = find_partname_length(currptr);
875     const char * start = skip_spaces(currptr + partnamelen);
876     if (*start != '{') { // first non-space after partname should be a {
877 #if COIN_DEBUG
878       SoDebugError::postWarning("SoBaseKit::set",
879                                 "parse error at byte %d in input string",
880                                 start-namevaluepairliststring);
881 #endif // COIN_DEBUG
882       return FALSE;
883     }
884     start++; // skip {
885     SbString partname(currptr, 0, partnamelen-1);
886     SoBaseKit * kit = this;
887     int partNum;
888     SbBool isList;
889     int listIdx;
890     if (!SoBaseKit::findPart(partname, kit, partNum, isList, listIdx, TRUE, NULL, TRUE)) {
891 #if COIN_DEBUG
892       SoDebugError::postWarning("SoBaseKit::set",
893                                 "part ``%s'' not found",
894                                 partname.getString());
895 #endif // COIN_DEBUG
896       return FALSE;
897     }
898 
899     SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
900     PRIVATE(kit)->instancelist[partNum]->setDefault(FALSE);
901 
902     if (isList) {
903       SoNodeKitListPart * list = (SoNodeKitListPart *)node;
904       if (listIdx < 0 || listIdx > list->getNumChildren()) {
905 #if COIN_DEBUG
906         SoDebugError::postWarning("SoBaseKit::set",
907                                   "index %d out of bounds for part ``%s''",
908                                   listIdx, partname.getString());
909 #endif // COIN_DEBUG
910         return FALSE;
911       }
912       else if (listIdx == list->getNumChildren()) {
913         if (!list->canCreateDefaultChild()) {
914 #if COIN_DEBUG
915           SoDebugError::postWarning("SoBaseKit::set",
916                                     "Unable to create default child for list-part ``%s''",
917                                     partname.getString());
918 #endif // COIN_DEBUG
919           return FALSE;
920         }
921         node = list->createAndAddDefaultChild();
922       }
923       else {
924         node = list->getChild(listIdx);
925       }
926     }
927     memInput.setBuffer((void *)start, stringlen - (start-namevaluepairliststring));
928     SbBool dummy;
929     if (!node->getFieldData()->read(&memInput, node, TRUE, dummy)) {
930 #if COIN_DEBUG
931       SoDebugError::postWarning("SoBaseKit::set",
932                                 "error while parsing data for part ``%s''",
933                                 partname.getString());
934 #endif // COIN_DEBUG
935       return FALSE;
936     }
937     currptr = start + (int) memInput.getNumBytesRead();
938     if (*currptr == '}') currptr++;
939     assert(currptr <= namevaluepairliststring + stringlen);
940     currptr = skip_spaces(currptr);
941   }
942   return TRUE;
943 }
944 
945 /*!
946   This just overloads the other SoBaseKit::set() method, and provides
947   a way to set a part value by using a separate input argument for the
948   name of the part and the name of the field (i.e. parameter)
949   settings.
950 */
951 SbBool
set(const char * partnamestring,const char * parameterstring)952 SoBaseKit::set(const char * partnamestring, const char * parameterstring)
953 {
954   SbString partname(partnamestring);
955   int partNum;
956   SbBool isList;
957   int listIdx;
958   SoBaseKit * kit = this;
959   if (SoBaseKit::findPart(partname, kit, partNum, isList, listIdx, TRUE, NULL, TRUE)) {
960     SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
961     PRIVATE(kit)->instancelist[partNum]->setDefault(FALSE);
962     assert(node != NULL); // makeifneeded was TRUE in findPart call
963     if (isList) {
964       assert(node->isOfType(SoNodeKitListPart::getClassTypeId()));
965       SoNodeKitListPart * list = (SoNodeKitListPart *) node;
966       if (listIdx < 0 || listIdx > list->getNumChildren()) {
967 #if COIN_DEBUG
968         SoDebugError::postWarning("SoBaseKit::set",
969                                   "index %d out of bounds for part ``%s''",
970                                   listIdx, partnamestring);
971 #endif // COIN_DEBUG
972         return FALSE;
973       }
974       else if (listIdx == list->getNumChildren()) {
975         if (!list->canCreateDefaultChild()) {
976 #if COIN_DEBUG
977           SoDebugError::postWarning("SoBaseKit::set",
978                                     "Unable to create default child for list-part ``%s''",
979                                     partname.getString());
980 #endif // COIN_DEBUG
981           return FALSE;
982         }
983         node = list->createAndAddDefaultChild();
984       }
985       else {
986         node = list->getChild(listIdx);
987       }
988     }
989     if (node) {
990       SoInput memInput;
991       SbBool dummy;
992       memInput.setBuffer((void *)parameterstring, strlen(parameterstring));
993       const SoFieldData * fielddata = node->getFieldData();
994       return fielddata->read(&memInput, node, TRUE, dummy);
995     }
996   }
997   return FALSE;
998 }
999 
1000 // Doc in superclass.
1001 void
doAction(SoAction * action)1002 SoBaseKit::doAction(SoAction * action)
1003 {
1004   int numindices;
1005   const int * indices;
1006   if (action->getPathCode(numindices, indices) == SoAction::IN_PATH) {
1007     this->children->traverseInPath(action, numindices, indices);
1008   }
1009   else {
1010     this->children->traverse(action);
1011   }
1012 }
1013 
1014 // Doc in superclass.
1015 void
callback(SoCallbackAction * action)1016 SoBaseKit::callback(SoCallbackAction * action)
1017 {
1018   SoBaseKit::doAction((SoAction *)action);
1019 }
1020 
1021 // Doc in superclass.
1022 void
GLRender(SoGLRenderAction * action)1023 SoBaseKit::GLRender(SoGLRenderAction * action)
1024 {
1025   SoBaseKit::doAction((SoAction *)action);
1026 }
1027 
1028 // Doc in superclass. Overriden to calculate bounding box center.
1029 void
getBoundingBox(SoGetBoundingBoxAction * action)1030 SoBaseKit::getBoundingBox(SoGetBoundingBoxAction * action)
1031 {
1032   int numindices;
1033   const int * indices;
1034   int last = action->getPathCode(numindices, indices) == SoAction::IN_PATH ?
1035     indices[numindices-1] : this->children->getLength() - 1;
1036 
1037   SbVec3f acccenter(0.0f, 0.0f, 0.0f);
1038   int numacc = 0;
1039 
1040   for (int i = 0; i <= last; i++) {
1041     this->children->traverse(action, i, i);
1042     if (action->isCenterSet()) {
1043       acccenter += action->getCenter();
1044       numacc++;
1045       action->resetCenter();
1046     }
1047   }
1048   if (numacc) action->setCenter(acccenter / float(numacc), FALSE);
1049 }
1050 
1051 // Doc in superclass.
1052 void
getMatrix(SoGetMatrixAction * action)1053 SoBaseKit::getMatrix(SoGetMatrixAction * action)
1054 {
1055   // SoBaseKit should be travesed like a normal SoGroup node, and the
1056   // children should only be traversed if we're IN_PATH or OFF_PATH
1057   // (SoGetMatrixAction is only applied on a path or on a single node,
1058   // and we must not calculate when BELOW_PATH or NO_PATH).
1059   int numindices;
1060   const int * indices;
1061   switch (action->getPathCode(numindices, indices)) {
1062   case SoAction::IN_PATH:
1063     this->children->traverseInPath(action, numindices, indices);
1064     break;
1065   case SoAction::OFF_PATH:
1066     this->children->traverse(action);
1067     break;
1068   default:
1069     break;
1070   }
1071 }
1072 
1073 // Doc in superclass.
1074 void
handleEvent(SoHandleEventAction * action)1075 SoBaseKit::handleEvent(SoHandleEventAction * action)
1076 {
1077   SoBaseKit::doAction((SoAction *)action);
1078 }
1079 
1080 // Doc in superclass.
1081 void
rayPick(SoRayPickAction * action)1082 SoBaseKit::rayPick(SoRayPickAction * action)
1083 {
1084   SoBaseKit::doAction((SoAction *)action);
1085 
1086   const SoPickedPointList & pplist = action->getPickedPointList();
1087   const int n = pplist.getLength();
1088   for (int i = 0; i < n; i++) {
1089     SoPickedPoint * pp = pplist[i];
1090     SoFullPath * path = (SoFullPath*) pp->getPath();
1091     if (path->containsNode(this) && pp->getDetail(this) == NULL) {
1092       PRIVATE(this)->addKitDetail(path, pp);
1093     }
1094   }
1095 }
1096 
1097 // Doc in superclass.
1098 void
search(SoSearchAction * action)1099 SoBaseKit::search(SoSearchAction * action)
1100 {
1101   inherited::search(action);
1102   if (action->isFound() || !SoBaseKit::searchchildren) return;
1103   SoBaseKit::doAction((SoAction *)action);
1104 }
1105 
1106 // Test if node has all fields set to default and if the fields
1107 // contains the default values. If so, we don't need to write it.
1108 static SbBool
is_default_node(SoNode * node)1109 is_default_node(SoNode * node)
1110 {
1111   SoNode * definstance = NULL;
1112   const SoFieldData * fielddata = node->getFieldData();
1113   int i, n = fielddata->getNumFields();
1114   for (i = 0; i < n; i++) {
1115     SoField * field = fielddata->getField(node, i);
1116     if (field->isConnectionEnabled() && field->isConnected()) break;
1117     if (definstance == NULL) {
1118       definstance = (SoNode *)node->getTypeId().createInstance();
1119       definstance->ref();
1120     }
1121     if (!field->isDefault() &&
1122         !field->isSame(*fielddata->getField(definstance, i))) break;
1123   }
1124   if (definstance) definstance->unref();
1125   // if all fields were tested, it is a default node.
1126   return i == n;
1127 }
1128 
1129 // Doc in superclass.
1130 void
write(SoWriteAction * action)1131 SoBaseKit::write(SoWriteAction * action)
1132 {
1133   // debugging code start **************************************************
1134 
1135   // If the below envvar is set, we'll write nodekit's current scene
1136   // graph instead of writing as a nodekit.
1137   //
1138   // Note that if the nodekit is a dragger, the resulting scene graph
1139   // export may still not look exactly the same as when the exported
1140   // sub-graph is contained within the dragger, as
1141   // SoDragger::GLRender() sets a number of elements in the traversal
1142   // state to non-intrusive "default" values before rendering the
1143   // dragger geometry. These settings will not be part of the exported
1144   // iv-file.
1145   static int dump = -1;
1146   if (dump == -1) {
1147     const char * env = coin_getenv("COIN_DEBUG_FLATTEN_NODEKITS_ON_WRITE");
1148     dump = env && (atoi(env) > 0);
1149   }
1150   if (dump) {
1151     this->children->traverse(action);
1152     return;
1153   }
1154 
1155   // debugging code end ****************************************************
1156 
1157 
1158   SoOutput * out = action->getOutput();
1159   if (out->getStage() == SoOutput::COUNT_REFS) {
1160     this->addWriteReference(out, FALSE);
1161   }
1162   else if (out->getStage() == SoOutput::WRITE) {
1163     if (this->writeHeader(out, FALSE, FALSE)) return; // no more to write
1164     // FIXME: shouldn't this if() rather be an assert? 20030523 mortene.
1165     if (PRIVATE(this)->writedata) {
1166       PRIVATE(this)->writedata->write(out, this);
1167       // we don't need it any more
1168       delete PRIVATE(this)->writedata;
1169       PRIVATE(this)->writedata = NULL;
1170     }
1171     this->writeFooter(out);
1172   }
1173   else assert(0 && "unknown stage");
1174 }
1175 
1176 // documented in superclass
1177 void
addWriteReference(SoOutput * out,SbBool isfromfield)1178 SoBaseKit::addWriteReference(SoOutput * out, SbBool isfromfield)
1179 {
1180   // don't call inherited::addWriteReference(), as we will handle
1181   // the fields ourselves, using a new fielddata. This is needed to
1182   // write fields in the correct order.
1183   SoBase::addWriteReference(out, isfromfield);
1184 
1185   // If first invocation during the reference counting pass, check
1186   // nodes in our catalog.
1187   if (!isfromfield && !SoWriterefCounter::instance(out)->hasMultipleWriteRefs(this)) {
1188     this->countMyFields(out);
1189   }
1190 }
1191 
1192 /*!
1193   Reference count the write connections to nodes in the catalog.
1194 */
1195 void
countMyFields(SoOutput * out)1196 SoBaseKit::countMyFields(SoOutput * out)
1197 {
1198   assert(out->getStage() == SoOutput::COUNT_REFS);
1199 
1200   // already created?
1201   //
1202   // FIXME: could this ever be TRUE without that being an error
1203   // situation? I have a feeling this should rather be an
1204   // assert(). Investigate. 20030523 mortene.
1205   if (PRIVATE(this)->writedata) return;
1206 
1207   // Initialize isDefault() flag on fields that should not be
1208   // written. This is a virtual method.
1209   this->setDefaultOnNonWritingFields();
1210 
1211   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
1212 
1213   // PRIVATE(this)->writedata contains a sorted list of fields.
1214   //
1215   // FIXME: the pimpl->writedata scheme doesn't look multithread-safe
1216   // wrt multiple SoWriteAction instances working in parallel over the
1217   // same scene. 20030521 mortene.
1218   PRIVATE(this)->createWriteData();
1219 
1220   // test if parent of parts is writing. Then we must write part anyway.
1221   PRIVATE(this)->testParentWrite();
1222 
1223   // we might count fields that won't be written here, but it
1224   // doesn't matter, since we're operating on a copy of the fields.
1225 
1226   int i, n = PRIVATE(this)->writedata->getNumFields();
1227   for (i = 0; i < n; i++) {
1228     const SbName name = PRIVATE(this)->writedata->getFieldName(i);
1229     SoField * field = PRIVATE(this)->writedata->getField(this, i);
1230     int partnum = catalog->getPartNumber(name);
1231     if (partnum < 0) {
1232       // field is not a part. Do normal field write.
1233       if (field->shouldWrite()) {
1234         field->write(out, name);
1235       }
1236     }
1237     else {
1238       if (!field->isDefault()) field->write(out, name);
1239       else {
1240         SoNode * node = (SoNode*) ((SoSFNode*)field)->getValue();
1241         if (node) {
1242           if (node->isOfType(SoBaseKit::getClassTypeId())) {
1243             SoBaseKit * kit = (SoBaseKit*) node;
1244             kit->countMyFields(out);
1245             if (kit->forceChildDrivenWriteRefs(out)) {
1246               field->setDefault(FALSE);
1247               // add a write reference on the kit node only. We supply
1248               // isfromfield TRUE to achieve this
1249               kit->addWriteReference(out, TRUE);
1250             }
1251           }
1252         }
1253       }
1254     }
1255   }
1256 }
1257 
1258 // Note: the following documentation for
1259 // setDefaultOnNonWritingFields() will also be used for nodekit and
1260 // dragger subclasses, so keep it general.
1261 /*!
1262   (Be aware that this method is unlikely to be of interest to the
1263   application programmer who does not want to extend the library with
1264   new custom nodekits or draggers.  If you indeed \e are writing
1265   extensions, see the information in the SoBaseKit class
1266   documentation.)
1267 
1268   This is a virtual method, and the code in it should call
1269   SoField::setDefault() with argument \c TRUE on part fields that
1270   should not be written upon scenegraph export operations.
1271 
1272   This is typically done when:
1273 
1274   <OL>
1275 
1276   <LI> field value is \c NULL and part is \c NULL by default </LI>
1277 
1278   <LI> it is a leaf SoGroup or SoSeparator node with no children </LI>
1279 
1280   <LI> it is a leaf listpart with no children and an SoGroup or
1281   SoSeparator container </LI>
1282 
1283   <LI> it is a non-leaf part and it's of SoGroup type and all fields
1284   are at their default values </LI>
1285 
1286   </OL>
1287 
1288   Subclasses should usually override this to do additional settings
1289   for new member fields.  From the subclass, do remember to call
1290   "upwards" to your superclass' setDefaultOnNonWritingFields() method.
1291 */
1292 void
setDefaultOnNonWritingFields(void)1293 SoBaseKit::setDefaultOnNonWritingFields(void)
1294 {
1295   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
1296   int n = PRIVATE(this)->instancelist.getLength();
1297   for (int i = 1; i < n; i++) {
1298     SoSFNode * field = PRIVATE(this)->instancelist[i];
1299     if (field->isDefault()) { continue; }
1300 
1301     SoNode * node = field->getValue();
1302 
1303     if (node == NULL) {
1304       // first test listed in API doc above
1305       if (catalog->isNullByDefault(i)) { field->setDefault(TRUE); }
1306       continue;
1307     }
1308 
1309     const SbBool leaf = catalog->isLeaf(i);
1310     const SoType type = node->getTypeId();
1311 
1312     if (leaf) {
1313       // second test
1314       if ((type == SoGroup::getClassTypeId() ||
1315            type == SoSeparator::getClassTypeId()) &&
1316           ((SoGroup*)node)->getNumChildren() == 0) {
1317         field->setDefault(TRUE);
1318       }
1319       // third test
1320       else if (type == SoNodeKitListPart::getClassTypeId()) {
1321         SoNodeKitListPart * list = (SoNodeKitListPart*) node;
1322         const SoNode * container = list->getContainerNode();
1323         if (list->getNumChildren() == 0 && container &&
1324             (container->getTypeId() == SoSeparator::getClassTypeId() ||
1325              container->getTypeId() == SoGroup::getClassTypeId())) {
1326           field->setDefault(TRUE);
1327         }
1328       }
1329     }
1330     else { // not leaf
1331       // fourth test
1332       if (node->isOfType(SoGroup::getClassTypeId()) && is_default_node(node)) {
1333         field->setDefault(TRUE);
1334       }
1335     }
1336   }
1337 }
1338 
1339 /*!
1340   Returns \c TRUE if kit should write. This happens if shouldWrite()
1341   returns \c TRUE, or if any of the children (recursively) should
1342   write.
1343 */
1344 SbBool
forceChildDrivenWriteRefs(SoOutput * out)1345 SoBaseKit::forceChildDrivenWriteRefs(SoOutput * out)
1346 {
1347   if (SoWriterefCounter::instance(out)->shouldWrite(this)) return TRUE;
1348 
1349   // if NULL we already did this test, found that we shouldn't write,
1350   // deleted writedata and set writedata to NULL.
1351   if (!PRIVATE(this)->writedata) return FALSE;
1352 
1353   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
1354   int i, n = PRIVATE(this)->writedata->getNumFields();
1355 
1356   // loop through fields and break as soon as we find a reason
1357   // to write
1358   for (i = 0; i < n; i++) {
1359     SoField * field = PRIVATE(this)->writedata->getField(this, i);
1360     int partnum = catalog->getPartNumber(PRIVATE(this)->writedata->getFieldName(i));
1361     if (!field->isDefault()) break;
1362     else if (partnum < 0 && field->isIgnored()) break;
1363     else if (partnum > 0) {
1364       SoSFNode * part = (SoSFNode*) field;
1365       SoNode * node = part->getValue();
1366       if (node) {
1367         if (SoWriterefCounter::instance(out)->shouldWrite(node)) break;
1368         else if (node->isOfType(SoBaseKit::getClassTypeId())) {
1369           SoBaseKit * kit = (SoBaseKit*) node;
1370           // recurse
1371           if (kit->forceChildDrivenWriteRefs(out)) break;
1372         }
1373       }
1374     }
1375   }
1376 
1377   if (i < n) { // did we find a reason to write?
1378     SoBase::addWriteReference(out);
1379     return TRUE;
1380   }
1381   else {
1382     delete PRIVATE(this)->writedata;
1383     PRIVATE(this)->writedata = NULL;
1384     return FALSE;
1385   }
1386 }
1387 
1388 
1389 // Documented in superclass.
1390 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)1391 SoBaseKit::getPrimitiveCount(SoGetPrimitiveCountAction * action)
1392 {
1393   SoBaseKit::doAction((SoAction *)action);
1394 }
1395 
1396 // Documented in superclass.
1397 SoChildList *
getChildren(void) const1398 SoBaseKit::getChildren(void) const
1399 {
1400   return this->children;
1401 }
1402 
1403 /*!
1404   Print out the full nodekit catalog structure.  Just invokes
1405   SoBaseKit::printSubDiagram() on the catalog root. Useful for
1406   debugging.
1407 
1408   Example output:
1409 
1410   \verbatim
1411   CLASS SoWrapperKit
1412   -->"this"
1413         "callbackList"
1414         "topSeparator"
1415            "pickStyle"
1416            "appearance"
1417            "units"
1418            "transform"
1419            "texture2Transform"
1420            "childList"
1421   -->      "localTransform"
1422   -->      "contents"
1423   \endverbatim
1424 
1425   The arrows denote new entries in the catalog for the particular
1426   class versus it's superclass. (Apart from the root entry, of
1427   course.)
1428 
1429   For a more detailed catalog dump, see SoBaseKit::printTable().
1430 */
1431 void
printDiagram(void)1432 SoBaseKit::printDiagram(void)
1433 {
1434   fprintf(stdout, "CLASS So%s\n", this->getTypeId().getName().getString());
1435   this->printSubDiagram("this", 0);
1436 }
1437 
1438 /*!
1439   Print out the nodekit catalog structure from \a rootname and
1440   downwards in the catalog tree, with indentation starting at \a
1441   level.
1442 
1443   \sa printDiagram()
1444 */
1445 void
printSubDiagram(const SbName & rootname,int level)1446 SoBaseKit::printSubDiagram(const SbName & rootname, int level)
1447 {
1448   const SoNodekitCatalog * parentcatalog = NULL;
1449   if (this->getTypeId() != SoBaseKit::getClassTypeId()) {
1450     SoType parenttype = this->getTypeId().getParent();
1451     SoBaseKit * parentobj = (SoBaseKit *)parenttype.createInstance();
1452     parentcatalog = parentobj->getNodekitCatalog();
1453     parentobj->ref();
1454     parentobj->unref();
1455   }
1456 
1457   const SoNodekitCatalog * thiscat = this->getNodekitCatalog();
1458 
1459   int i = 0;
1460   if (!parentcatalog ||
1461       parentcatalog->getPartNumber(rootname) == SO_CATALOG_NAME_NOT_FOUND ||
1462       parentcatalog->getType(rootname) != thiscat->getType(rootname)) {
1463     fprintf(stdout, "-->");
1464     i++;
1465   }
1466   for (; i < level+1; i++) fprintf(stdout, "   ");
1467 
1468   fprintf(stdout, "\"%s\"\n", rootname.getString());
1469 
1470   for (int j=0; j < thiscat->getNumEntries(); j++) {
1471     if (thiscat->getParentName(j) == rootname)
1472       this->printSubDiagram(thiscat->getName(j), level + 1);
1473   }
1474 }
1475 
1476 /*!
1477   Write the complete nodekit catalog in table form.
1478 
1479   Example output:
1480 
1481   \verbatim
1482   CLASS SoWrapperKit
1483   PVT   "this",  SoWrapperKit  ---
1484         "callbackList",  SoNodeKitListPart [ SoCallback, SoEventCallback ]
1485   PVT   "topSeparator",  SoSeparator  ---
1486         "pickStyle",  SoPickStyle  ---
1487         "appearance",  SoAppearanceKit  ---
1488         "units",  SoUnits  ---
1489         "transform",  SoTransform  ---
1490         "texture2Transform",  SoTexture2Transform  ---
1491         "childList",  SoNodeKitListPart [ SoShapeKit, SoSeparatorKit ]
1492         "localTransform",  SoTransform  ---
1493         "contents",  SoSeparator  ---
1494   \endverbatim
1495 
1496   \c PVT denotes that it's a private entry in the catalog, then
1497   follows the part name and the part type. If the part is a list, the
1498   allowed node types for the list is given in square brackets, and if
1499   not there's a triple hyphen. If the part type is abstract, the
1500   default part type will be listed last (not shown in the example
1501   output above).
1502 */
1503 void
printTable(void)1504 SoBaseKit::printTable(void)
1505 {
1506   fprintf(stdout, "CLASS So%s\n", this->getTypeId().getName().getString());
1507 
1508   const SoNodekitCatalog * thiscat = this->getNodekitCatalog();
1509   for (int i=0; i < thiscat->getNumEntries(); i++) {
1510     const SoType t = thiscat->getType(i);
1511     fprintf(stdout, "%s   \"%s\",  So%s ",
1512             thiscat->isPublic(i) ? "   " : "PVT",
1513             thiscat->getName(i).getString(),
1514             t.getName().getString());
1515     if (thiscat->isList(i)) {
1516       SoTypeList tlist = thiscat->getListItemTypes(i);
1517       fprintf(stdout, "[ ");
1518       for (int j=0; j < tlist.getLength(); j++) {
1519         if (j) fprintf(stdout, ", ");
1520         fprintf(stdout, "So%s", tlist[j].getName().getString());
1521       }
1522       fprintf(stdout, " ] ");
1523     }
1524     else {
1525       fprintf(stdout, " --- ");
1526     }
1527 
1528     if (t != thiscat->getDefaultType(i)) {
1529       fprintf(stdout, ", (default type = So%s)",
1530               thiscat->getDefaultType(i).getName().getString());
1531     }
1532     fprintf(stdout, "\n");
1533   }
1534 }
1535 
1536 /*!
1537   Returns the value of the flag indicating whether or not the kit
1538   parts are searched during SoSearchAction traversal.
1539 
1540   \sa SoBaseKit::setSearchingChildren()
1541 */
1542 SbBool
isSearchingChildren(void)1543 SoBaseKit::isSearchingChildren(void)
1544 {
1545   return SoBaseKit::searchchildren;
1546 }
1547 
1548 /*!
1549   Set whether or not the kit parts should be searched during
1550   SoSearchAction traversal. The default value is \c FALSE.
1551 */
1552 void
setSearchingChildren(const SbBool newval)1553 SoBaseKit::setSearchingChildren(const SbBool newval)
1554 {
1555   SoBaseKit::searchchildren = newval;
1556 }
1557 
1558 // Documented in superclass. Overridden to also recurse on non-null
1559 // part nodes.
1560 SoNode *
addToCopyDict(void) const1561 SoBaseKit::addToCopyDict(void) const
1562 {
1563   SoNode * cp = (SoNode*) SoFieldContainer::checkCopy(this);
1564   if (cp == NULL) { // not copied?
1565     cp = (SoNode*) this->getTypeId().createInstance();
1566     assert(cp);
1567     cp->ref();
1568     SoFieldContainer::addCopy(this, cp);
1569     cp->unrefNoDelete();
1570 
1571     int n = PRIVATE(this)->instancelist.getLength();
1572     for (int i = 1; i < n; i++) {
1573       SoNode * node = PRIVATE(this)->instancelist[i]->getValue();
1574       if (node != NULL) node->addToCopyDict();
1575     }
1576   }
1577   return cp;
1578 }
1579 
1580 // (Doc in superclass.) Overridden to copy parts correctly.
1581 void
copyContents(const SoFieldContainer * fromfc,SbBool copyconnections)1582 SoBaseKit::copyContents(const SoFieldContainer * fromfc,
1583                         SbBool copyconnections)
1584 {
1585   int i;
1586 
1587   // disable connections while copying
1588   SbBool oldsetup = this->setUpConnections(FALSE);
1589 
1590   // do normal node copy
1591   inherited::copyContents(fromfc, copyconnections);
1592 
1593   const SoBaseKit * srckit = (const SoBaseKit*) fromfc;
1594 
1595   // convenient reference
1596   /*const SbList <SoSFNode*> & srcfields =*/ srckit->getCatalogInstances();
1597 
1598   const int n = PRIVATE(this)->instancelist.getLength();
1599 
1600   // use temporary lists to store part node pointers and field
1601   // default flag, as we will modify the originals.
1602   SbList <SoNode *> partlist;
1603   SbList <SbBool> flaglist;
1604 
1605   // part 0 is this
1606   partlist.append(NULL);
1607   flaglist.append(FALSE);
1608 
1609   // initialize temporary lists
1610   for (i = 1; i < n; i++) {
1611     partlist.append(NULL);
1612     flaglist.append(PRIVATE(this)->instancelist[i]->isDefault());
1613   }
1614 
1615   // copy parts, taking care of scene graph
1616   PRIVATE(this)->copyParts(srckit, partlist, copyconnections);
1617 
1618   // remove all old children before copying parts
1619   this->getChildren()->truncate(0);
1620 
1621   // reset part fields
1622   for (i = 1; i < n; i++) {
1623     PRIVATE(this)->instancelist[i]->setValue(NULL);
1624     PRIVATE(this)->instancelist[i]->setDefault(TRUE);
1625   }
1626 
1627   // set non-leaf nodes first
1628   PRIVATE(this)->setParts(partlist, FALSE);
1629 
1630   // then leaf nodes
1631   PRIVATE(this)->setParts(partlist, TRUE);
1632 
1633   // do final pass
1634   for (i = 1; i < n; i++) {
1635     // restore default flag for fields
1636     PRIVATE(this)->instancelist[i]->setDefault(flaglist[i]);
1637 
1638     // unref nodes in temporary list as they were ref'ed
1639     // when inserted
1640     if (partlist[i]) partlist[i]->unref();
1641   }
1642 
1643   // enable connections
1644   if (oldsetup) this->setUpConnections(TRUE);
1645 }
1646 
1647 /*!
1648   Returns a pointer to the group node above an SoNodeKitListPart in
1649   the catalog given by \a listname.
1650 
1651   If the list part (and its container) was not yet constructed, they
1652   will be so if \a makeifneeded is \c TRUE (otherwise, \c NULL will be
1653   returned).
1654 */
1655 SoGroup *
getContainerNode(const SbName & listname,SbBool makeifneeded)1656 SoBaseKit::getContainerNode(const SbName & listname, SbBool makeifneeded)
1657 {
1658   SoBaseKit * kit = this;
1659   int partNum;
1660   SbBool isList;
1661   int listIdx;
1662   if (SoBaseKit::findPart(SbString(listname.getString()), kit, partNum,
1663                           isList, listIdx, makeifneeded, NULL, TRUE)) {
1664     SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
1665     if (node == NULL) return NULL;
1666     assert(node->isOfType(SoNodeKitListPart::getClassTypeId()));
1667     SoNodeKitListPart * list = (SoNodeKitListPart *)node;
1668     return list->getContainerNode();
1669   }
1670   return NULL;
1671 }
1672 
1673 /*!
1674   Returns catalog part of the given \a partname.
1675 
1676   If the \a partname part is not in the nodekit's catalog, return \c
1677   NULL.
1678 
1679   If the part is specified in the catalog, but has not yet been made,
1680   the function will either construct the part (if \a makeifneeded is
1681   \c TRUE) or just return \c NULL (if \a makeifneeded is \c FALSE).
1682 
1683   If \a leafcheck is \c TRUE, a pointer to the part will only be
1684   returned if it's a leaf in the catalog (otherwise \c NULL is
1685   returned).
1686 
1687   If \a publiccheck is \c TRUE, a pointer to the part will only be
1688   returned if it's a public catalog part (otherwise \c NULL is
1689   returned).
1690 
1691 
1692   The \a partname input argument should be given as a \e "path" of
1693   catalog part names down to the wanted leaf part. The syntax for
1694   specifiying \a partname "paths" is as follows (given in Backus-Naur
1695   Form (BNF)):
1696 
1697   \verbatim
1698   BNF:
1699 
1700   partname = singlename | compoundname
1701   compoundname = singlename | compoundname.singlename
1702   singlename = singlepartname | singlelistelementname
1703   singlelistelementname = singlelistname[idx]
1704 
1705   singlepartname is name of a part ("ordinary", nodekit or list)
1706   singlelistname is name of a part which is a list
1707   idx is an integer value
1708   \endverbatim
1709 */
1710 SoNode *
getAnyPart(const SbName & partname,SbBool makeifneeded,SbBool leafcheck,SbBool publiccheck)1711 SoBaseKit::getAnyPart(const SbName & partname, SbBool makeifneeded,
1712                       SbBool leafcheck, SbBool publiccheck)
1713 {
1714 
1715   SoBaseKit * kit = this;
1716   int partNum;
1717   SbBool isList;
1718   int listIdx;
1719 
1720   SbString partstring(partname.getString());
1721 
1722   if (SoBaseKit::findPart(partstring, kit, partNum, isList, listIdx,
1723                           makeifneeded, NULL, TRUE)) {
1724 
1725     if (publiccheck && !kit->getNodekitCatalog()->isPublic(partNum)) {
1726       SoDebugError::postWarning("SoBaseKit::getAnyPart",
1727                                 "Part ``%s'' found in %s, but access is private.",
1728                                 partname.getString(),
1729                                 this->getTypeId().getName().getString());
1730       return NULL;
1731     }
1732 
1733     if (!leafcheck || kit->getNodekitCatalog()->isLeaf(partNum)) {
1734       if (isList) {
1735         SoNode * partnode = PRIVATE(kit)->instancelist[partNum]->getValue();
1736         if (partnode == NULL) return NULL;
1737         assert(partnode->isOfType(SoNodeKitListPart::getClassTypeId()));
1738         SoNodeKitListPart * list = (SoNodeKitListPart *) partnode;
1739         if (listIdx >= 0 && listIdx < list->getNumChildren()) {
1740           return list->getChild(listIdx);
1741         }
1742         else if (makeifneeded && (listIdx == list->getNumChildren())) {
1743           if (!list->canCreateDefaultChild()) {
1744 #if COIN_DEBUG
1745             SoDebugError::postWarning("SoBaseKit::getAnyPart",
1746                                       "Unable to create default child for list-part ``%s''",
1747                                       partname.getString());
1748 #endif // COIN_DEBUG
1749           }
1750           return list->createAndAddDefaultChild();
1751         }
1752         else {
1753 #if COIN_DEBUG
1754           SoDebugError::postWarning("SoBaseKit::getAnyPart",
1755                                     "index %d out of bounds for part ``%s''",
1756                                     listIdx, partname.getString());
1757 #endif // COIN_DEBUG
1758         }
1759       }
1760       else {
1761         return PRIVATE(kit)->instancelist[partNum]->getValue();
1762       }
1763     }
1764   }
1765 
1766   // FIXME:
1767   // run cleanup?, in case some node has been temporarily created while
1768   // searching for the part?? pederb, 2000-01-05
1769 
1770 #if COIN_DEBUG
1771   if (makeifneeded) { // user probably expected part to be found, post a warning
1772     SoDebugError::postWarning("SoBaseKit::getAnyPart",
1773                               "part ``%s'' not found in %s",
1774                               partname.getString(),
1775                               this->getTypeId().getName().getString());
1776   }
1777 #endif // COIN_DEBUG
1778   return NULL;
1779 }
1780 
1781 /*!
1782   Return path with nested SoNodeKit instances down in the catalog
1783   hierarchy given by \a partname.
1784 
1785   If the trailing part has not been made and \a makeifneeded is \c
1786   TRUE, make an instance of the part type and insert into the catalog,
1787   as done in setAnyPart().
1788 
1789   If \a leafcheck is \c TRUE, ignore non-leaf catalog node entries. If
1790   \a publiccheck is \c TRUE, ignore private catalog entries.
1791 
1792   \a pathtoextend is a path through the nodekit instance catalog
1793   hierarchy, where we should pick up and continue to create the path
1794   from where \a pathtoextend terminates. If \a pathtoextend is \c
1795   NULL, we simply start at the "this" toplevel node.
1796 
1797   Returns \c NULL on failure, for any of the possible reasons
1798   described above (part ends in non-leaf or private catalog entry,
1799   part is not syntactically valid or refers to non-existing catalog
1800   entries).
1801 */
1802 SoNodeKitPath *
createPathToAnyPart(const SbName & partname,SbBool makeifneeded,SbBool leafcheck,SbBool publiccheck,const SoPath * pathtoextend)1803 SoBaseKit::createPathToAnyPart(const SbName & partname, SbBool makeifneeded,
1804                                SbBool leafcheck, SbBool publiccheck,
1805                                const SoPath * pathtoextend)
1806 {
1807   SoFullPath * path;
1808   if (pathtoextend) {
1809     path = (SoFullPath *)pathtoextend->copy();
1810     path->ref();
1811     // pop off nodes beyond this kit node
1812     if (path->containsNode(this)) while (path->getTail() != this && path->getLength()) path->pop();
1813     else if (path->getLength()) {
1814       SoNode * node = path->getTail();
1815       if (!node->getChildren() || node->getChildren()->find(this) < 0) {
1816 #if COIN_DEBUG
1817         SoDebugError::postWarning("SoBaseKit::createPathToAnyPart",
1818                                   "pathtoextend is illegal");
1819 #endif // COIN_DEBUG
1820         path->unref();
1821         return NULL;
1822       }
1823       path->append(this); // this should be safe now
1824     }
1825   }
1826   else {
1827     path = (SoFullPath *)new SoPath(this);
1828     path->ref();
1829   }
1830 
1831   SoBaseKit * kit = this;
1832   int partNum;
1833   SbBool isList;
1834   int listIdx;
1835 
1836   if (SoBaseKit::findPart(SbString(partname.getString()), kit, partNum,
1837                           isList, listIdx, makeifneeded, path)) {
1838     const SoNodekitCatalog * catalog = kit->getNodekitCatalog();
1839     if ((leafcheck && ! catalog->isLeaf(partNum)) ||
1840         (publiccheck && ! catalog->isPublic(partNum))) {
1841       path->unref();
1842       return NULL;
1843     }
1844 
1845     SoNode * node = PRIVATE(kit)->instancelist[partNum]->getValue();
1846     if (node) {
1847       path->append(node);
1848       if (isList) {
1849         SoNodeKitListPart * list = (SoNodeKitListPart *)node;
1850         int numlistchildren = list->getNumChildren();
1851         if (listIdx < 0 || listIdx > numlistchildren || (!makeifneeded && listIdx == numlistchildren)) {
1852 #if COIN_DEBUG
1853           SoDebugError::postWarning("SoBaseKit::createPathToAnyPart",
1854                                     "index %d out of bounds for part ``%s''",
1855                                     listIdx, partname.getString());
1856 #endif // COIN_DEBUG
1857           path->unref();
1858           return NULL;
1859         }
1860         else if (listIdx == numlistchildren) {
1861           if (!list->canCreateDefaultChild()) {
1862 #if COIN_DEBUG
1863             SoDebugError::postWarning("SoBaseKit::createPathToAnyPart",
1864                                       "Unable to create default child for list-part ``%s''",
1865                                       partname.getString());
1866 #endif //COIN_DEBUG
1867 
1868           }
1869           else {
1870             SoNode * newnode = list->createAndAddDefaultChild();
1871             path->append(list->getContainerNode());
1872             path->append(newnode);
1873           }
1874         }
1875         else {
1876           path->append(list->getContainerNode());
1877           path->append(list->getChild(listIdx));
1878         }
1879       }
1880       path->unrefNoDelete();
1881       return (SoNodeKitPath *)path;
1882     }
1883   }
1884   path->unref();
1885   return NULL;
1886 }
1887 
1888 /*!
1889   \COININTERNAL
1890 */
1891 SbBool
setAnyPart(const SbName & partname,SoNode * from,SbBool anypart)1892 SoBaseKit::setAnyPart(const SbName & partname, SoNode * from, SbBool anypart)
1893 {
1894   SoBaseKit * kit = this;
1895   int partNum;
1896   SbBool isList;
1897   int listIdx;
1898 
1899   SbString partstring(partname.getString());
1900 
1901   // FIXME: findPart() really needs another parameter, since we need
1902   // to create intermediate parts, but not the leaf part. For now we
1903   // just supply makeifneeded = TRUE, and then immediately overwrite
1904   // the part here. pederb, 2004-06-07
1905   if (SoBaseKit::findPart(partstring, kit, partNum, isList, listIdx, TRUE, NULL, TRUE)) {
1906     if (anypart || kit->getNodekitCatalog()->isPublic(partNum)) {
1907       if (isList) {
1908         SoNode * partnode = PRIVATE(kit)->instancelist[partNum]->getValue();
1909         if (partnode) {
1910           assert(partnode->isOfType(SoNodeKitListPart::getClassTypeId()));
1911           SoNodeKitListPart * list = (SoNodeKitListPart *) partnode;
1912           if (listIdx >= 0 && listIdx <= list->getNumChildren()) {
1913             if (listIdx == list->getNumChildren())
1914               list->addChild(from);
1915             else
1916               list->replaceChild(listIdx, from);
1917             return TRUE;
1918           }
1919           else {
1920 #if COIN_DEBUG
1921             SoDebugError::postWarning("SoBaseKit::setAnyPart",
1922                                       "index %d out of bounds for part ``%s''",
1923                                       listIdx, partname.getString());
1924 #endif // COIN_DEBUG
1925           }
1926         }
1927       }
1928       else {
1929         return kit->setPart(partNum, from);
1930       }
1931     }
1932     else {
1933 #if COIN_DEBUG
1934       SoDebugError::postWarning("SoBaseKit::setAnyPart",
1935                                 "attempted to set non-public part ``%s''",
1936                                 partname.getString());
1937 #endif // COIN_DEBUG
1938     }
1939   }
1940 #if COIN_DEBUG
1941   else {
1942     SoDebugError::postWarning("SoBaseKit::setAnyPart",
1943                               "part '%s' not found in %s",
1944                               partname.getString(),
1945                               this->getTypeId().getName().getString());
1946   }
1947 #endif // COIN_DEBUG
1948 
1949   // FIXME:
1950   // run cleanup, in case some node has been temporarily created while
1951   // searching for the part?? pederb, 2000-01-05
1952   return FALSE;
1953 }
1954 
1955 /*!
1956   Not part of the Coin API.
1957 
1958   It is supposed to create the SoNodekitParts class instance. Since
1959   this class can only be used by SoBaseKit (all members are private,
1960   with SoBaseKit as friend), we decided to not support this class, and
1961   solve the problem of recording which parts are created in an
1962   alternative way.
1963 */
1964 void
createNodekitPartsList(void)1965 SoBaseKit::createNodekitPartsList(void)
1966 {
1967   assert(0 &&
1968          "SoBaseKit::createNodekitPartsList() should not be used with Coin");
1969 }
1970 
1971 /*!
1972   Replaces the createNodekitPartsList() method.
1973 
1974   Sets up the list of SoSFNode fields with node pointers to the
1975   instances in our catalog.
1976 */
1977 void
createFieldList(void)1978 SoBaseKit::createFieldList(void)
1979 {
1980   // FIXME:
1981   // is there any way to make sure this code is only run once, and in
1982   // the top level constructor. pederb, 2000-01-06
1983   //
1984   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
1985   // only do this if the catalog has been created
1986   if (catalog) {
1987     PRIVATE(this)->instancelist.truncate(0);
1988     PRIVATE(this)->instancelist.append(NULL); // first catalog entry is "this"
1989     for (int i = 1; i < catalog->getNumEntries(); i++) {
1990       PRIVATE(this)->instancelist.append((SoSFNode *)this->getField(catalog->getName(i)));
1991       assert(PRIVATE(this)->instancelist[i] != NULL);
1992     }
1993   }
1994 }
1995 
1996 /*!
1997   \COININTERNAL
1998 */
1999 void
createDefaultParts(void)2000 SoBaseKit::createDefaultParts(void)
2001 {
2002   // FIXME:
2003   // is there any way to make sure this code is only run once, and in
2004   // the top level constructor. pederb, 2000-01-06
2005   //
2006   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
2007   // only do this if the catalog has been created
2008   if (catalog) {
2009     for (int i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
2010       if (!catalog->isNullByDefault(i)) {
2011         SoNode * old = PRIVATE(this)->instancelist[i]->getValue();
2012         if ((old == NULL || ! old->isOfType(catalog->getDefaultType(i)) )) {
2013           this->makePart(i);
2014           PRIVATE(this)->instancelist[i]->setDefault(TRUE);
2015         }
2016       }
2017     }
2018   }
2019 }
2020 
2021 /*!
2022   In Open Inventor, this method returns a pointer to a private class.
2023   It will always return \c NULL in Coin.
2024 
2025   \sa createNodekitPartsList()
2026 */
2027 const SoNodekitParts *
getNodekitPartsList(void) const2028 SoBaseKit::getNodekitPartsList(void) const
2029 {
2030   assert(0 &&
2031          "SoBaseKit::getNodekitPartsList() obsoleted in Coin");
2032   return NULL;
2033 }
2034 
2035 /*!
2036   \COININTERNAL
2037 */
2038 const SbList<SoSFNode*> &
getCatalogInstances(void) const2039 SoBaseKit::getCatalogInstances(void) const
2040 {
2041 //    return this->fieldList;
2042   return PRIVATE(this)->instancelist;
2043 }
2044 
2045 /*!
2046   Obsoleted from the API in Coin.
2047 */
2048 void
catalogError(void)2049 SoBaseKit::catalogError(void)
2050 {
2051   COIN_OBSOLETED();
2052 }
2053 
2054 // Note: the following documentation for setUpConnections() will also
2055 // be visible for subclass nodekits and draggers, so keep it general.
2056 /*!
2057   Sets up all internal connections for instances of this class.
2058 
2059   (This method will usually not be of interest to the application
2060   programmer, unless you want to extend the library with new custom
2061   nodekits or dragger classes.  If so, see the SoBaseKit class
2062   documentation.)
2063 */
2064 SbBool
setUpConnections(SbBool COIN_UNUSED_ARG (onoff),SbBool COIN_UNUSED_ARG (doitalways))2065 SoBaseKit::setUpConnections(SbBool COIN_UNUSED_ARG(onoff), SbBool COIN_UNUSED_ARG(doitalways))
2066 {
2067   return this->connectionsSetUp;
2068 }
2069 
2070 // doc in super
2071 SbBool
readInstance(SoInput * in,unsigned short COIN_UNUSED_ARG (flags))2072 SoBaseKit::readInstance(SoInput * in, unsigned short COIN_UNUSED_ARG(flags))
2073 {
2074   int i;
2075 
2076   SbBool oldnotify = this->enableNotify(FALSE);
2077   SbBool oldsetup = this->setUpConnections(FALSE);
2078 
2079   // store old part values to find which parts are read
2080   SoNodeList nodelist;
2081   SbList <SbBool> defaultlist;
2082 
2083   const SoNodekitCatalog * cat = this->getNodekitCatalog();
2084 
2085   // Dummy first element to get indices to match instancelist (where
2086   // the dummy "this" catalog entry is first).
2087   nodelist.append(NULL);
2088   defaultlist.append(FALSE);
2089 
2090   // copy all parts into nodelist, and then set all parts to NULL
2091   // and default before reading
2092   for (i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
2093     nodelist.append(PRIVATE(this)->instancelist[i]->getValue());
2094     defaultlist.append(PRIVATE(this)->instancelist[i]->isDefault());
2095     PRIVATE(this)->instancelist[i]->setValue(NULL);
2096     PRIVATE(this)->instancelist[i]->setDefault(TRUE);
2097   }
2098 
2099   // reset the node kit by removing all children. We will restore it
2100   // by setting the parts again later
2101   this->getChildren()->truncate(0);
2102 
2103   // actually read the nodekit.
2104   // Use readUnknownFields instead to read fields not part of catalog
2105   // SbBool ret = inherited::readInstance(in, flags);
2106 
2107   // Fields that's not part of catalog is read as a SoSFNode, and stored
2108   // in unknownfielddata. Later they'll be put in nodekit using setAnyPart.
2109   SbBool ret = TRUE;
2110   SoFieldData * unknownfielddata = new SoFieldData;
2111   if (!PRIVATE(this)->readUnknownFields(in, unknownfielddata))
2112     ret = FALSE;
2113 
2114   if (ret) {
2115     // loop through fields and copy the read parts into nodelist
2116     for (i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
2117       if (!PRIVATE(this)->instancelist[i]->isDefault()) { // we've read a part
2118         nodelist.set(i, PRIVATE(this)->instancelist[i]->getValue());
2119         defaultlist[i] = FALSE;
2120         // set to NULL again so that setPart() will not get confused
2121         PRIVATE(this)->instancelist[i]->setValue(NULL);
2122       }
2123     }
2124 
2125     // restore the nodekit with all old and read parts
2126     for (i = 1; i < PRIVATE(this)->instancelist.getLength(); i++) {
2127       if (!cat->isLeaf(i) && nodelist[i]) {
2128         // if not leaf, remove all children. They will be re-added
2129         // later when the children parts are set.
2130         assert(nodelist[i]->isOfType(SoGroup::getClassTypeId()));
2131         SoGroup * g = (SoGroup*) nodelist[i];
2132         g->removeAllChildren();
2133       }
2134       this->setPart(i, nodelist[i]);
2135       PRIVATE(this)->instancelist[i]->setDefault(defaultlist[i]);
2136     }
2137 
2138     // put the unknown fields into nodekit using setAnyPart
2139     SbName partname;
2140     SoNode * pnode;
2141     SoSFNode * pfield;
2142     for (i = 0; i < unknownfielddata->getNumFields(); i++) {
2143       partname = unknownfielddata->getFieldName(i);
2144       pfield = (SoSFNode *) unknownfielddata->getField(this, i);
2145       pnode = pfield->getValue();
2146       this->setAnyPart(partname, pnode);
2147     }
2148   }
2149 
2150   delete unknownfielddata;
2151 
2152   (void) this->setUpConnections(oldsetup);
2153   (void) this->enableNotify(oldnotify);
2154 
2155   return ret;
2156 }
2157 
2158 //
2159 // recurse until not possible to split string any more, and return information
2160 // about part and the kit the part is found in.
2161 // Remember to set kit=this before calling this method, also remember that
2162 // kit might change during this search.
2163 //
2164 // compoundname parts are created during this search, so it might be necessary
2165 // to do a nodekit cleanup if part is not public, or if part is set to NULL.
2166 //
2167 //
2168 // if path != NULL, kit-nodes will be appended to the path during the search
2169 // The actual part is not added to the path. The head of the path should
2170 // be set to the kit-node performing the search.
2171 //
2172 SbBool
findPart(const SbString & partname,SoBaseKit * & kit,int & partnum,SbBool & islist,int & listidx,const SbBool makeifneeded,SoPath * path,const SbBool recsearch)2173 SoBaseKit::findPart(const SbString & partname, SoBaseKit *& kit, int & partnum,
2174                     SbBool & islist, int & listidx, const SbBool makeifneeded,
2175                     SoPath * path, const SbBool recsearch)
2176 {
2177   // BNF:
2178   //
2179   // partname = singlename | compoundname
2180   // compoundname = singlename | compoundname.singlename
2181   // singlename = singlepartname | singlelistelementname
2182   // singlelistelementname = singlelistname[idx]
2183   //
2184   // singlepartname is name of a part ("ordinary", nodekit or list)
2185   // singlelistname is name of a part which is a list
2186   // idx is an integer value
2187 
2188   if (partname == "this") {
2189     islist = FALSE;
2190     partnum = 0;
2191     return TRUE;
2192   }
2193 
2194   const char * stringptr = partname.getString();
2195   const char * periodptr = strchr(stringptr, '.'); // find first period
2196   const char * startbracket = strchr(stringptr, '[');
2197 
2198   if (periodptr && (startbracket > periodptr))
2199     startbracket = NULL; // will handle later
2200 
2201   islist = FALSE; // set to FALSE first
2202   SbString firstpartname;
2203   if (startbracket) { // get index
2204     long int listindex = strtol(startbracket+1, NULL, 10);
2205     if (listindex == LONG_MIN || listindex == LONG_MAX) {
2206 #if COIN_DEBUG
2207       SoDebugError::postWarning("SoBaseKit::findPart",
2208                                 "list index not properly specified");
2209 #endif // COIN_DEBUG
2210       return FALSE;
2211     }
2212     const ptrdiff_t endidx = startbracket - stringptr - 1;
2213     firstpartname = partname.getSubString(0, (int)endidx);
2214     listidx = (int) listindex;
2215     islist = TRUE;
2216   }
2217   else if (periodptr) {
2218     const ptrdiff_t endidx = periodptr - stringptr - 1;
2219     firstpartname = partname.getSubString(0, (int)endidx);
2220   }
2221   else firstpartname = partname;
2222 
2223   partnum = kit->getNodekitCatalog()->getPartNumber(firstpartname);
2224   if (partnum == SO_CATALOG_NAME_NOT_FOUND) {
2225     if (recsearch) { // search leaf nodekits for this part?
2226       SoBaseKit * orgkit = kit;
2227       assert(path == NULL); // should not do recsearch when creating path
2228       const SoNodekitCatalog * catalog = orgkit->getNodekitCatalog();
2229       for (int i = 1; i < PRIVATE(orgkit)->instancelist.getLength(); i++) {
2230         if (catalog->isLeaf(i) &&
2231             catalog->getType(i).isDerivedFrom(SoBaseKit::getClassTypeId())) {
2232           kit = (SoBaseKit *)PRIVATE(orgkit)->instancelist[i]->getValue();
2233           SbBool didexist = kit != NULL;
2234           if (!didexist) {
2235             if (!makeifneeded) continue;
2236             orgkit->makePart(i);
2237             kit = (SoBaseKit *)PRIVATE(orgkit)->instancelist[i]->getValue();
2238           }
2239           if (SoBaseKit::findPart(partname, kit, partnum, islist, listidx,
2240                                   makeifneeded, path, recsearch)) {
2241             return TRUE;
2242           }
2243           else if (!didexist) {
2244             // we created this part, remove it
2245             orgkit->setPart(i, NULL);
2246           }
2247         }
2248       }
2249       kit = orgkit; // return with an error in this kit
2250     }
2251     // nope, not found
2252     return FALSE;
2253   }
2254 
2255   assert(partnum < PRIVATE(kit)->instancelist.getLength());
2256   SoSFNode * nodefield = PRIVATE(kit)->instancelist[partnum];
2257   assert(nodefield);
2258 
2259   if (makeifneeded && nodefield->getValue() == NULL) {
2260     kit->makePart(partnum);
2261   }
2262 
2263   if (path) {
2264     const SoNodekitCatalog * catalog = kit->getNodekitCatalog();
2265     SbList <SoNode*> nodestopart;
2266     int parent = catalog->getParentPartNumber(partnum);
2267     while (parent > 0) {
2268       SoNode * node = PRIVATE(kit)->instancelist[parent]->getValue();
2269       if (node == NULL) {
2270         assert(makeifneeded == FALSE);
2271         break;
2272       }
2273       nodestopart.push(node);
2274       parent = catalog->getParentPartNumber(parent);
2275     }
2276     assert(parent == 0 || !makeifneeded);
2277     while (nodestopart.getLength()) {
2278       SoNode * node = nodestopart.pop();
2279       path->append(node);
2280     }
2281   }
2282 
2283   if (periodptr == NULL) {
2284     // singlename or singlelistname found, do not recurse any more
2285     return TRUE; // all info has been found, just return TRUE
2286   }
2287   else { // recurse
2288     SoNode * node = nodefield->getValue();
2289     if (node == NULL) return FALSE;
2290     const ptrdiff_t startidx = periodptr - stringptr + 1;
2291     SbString newpartname = partname.getSubString((int)startidx);
2292     if (islist) {
2293       SoNodeKitListPart * list = (SoNodeKitListPart *) node;
2294       int numlistchildren = list->getNumChildren();
2295       if (listidx < 0 || listidx > numlistchildren || (!makeifneeded && listidx == numlistchildren)) {
2296 #if COIN_DEBUG
2297         SoDebugError::postWarning("SoBaseKit::findPart",
2298                                   "index %d out of bounds for part ``%s''",
2299                                   listidx,
2300                                   firstpartname.getString());
2301 #endif // COIN_DEBUG
2302         return FALSE;
2303       }
2304       else if (listidx == numlistchildren) {
2305         (void) list->createAndAddDefaultChild();
2306       }
2307       SoNode * partnode = list->getChild(listidx);
2308       assert(partnode && partnode->isOfType(SoBaseKit::getClassTypeId()));
2309       kit = (SoBaseKit *)partnode;
2310 
2311       if (path) {
2312         path->append(list);
2313         path->append(list->getContainerNode());
2314       }
2315     }
2316     else {
2317       assert(node->isOfType(SoBaseKit::getClassTypeId()));
2318       kit = (SoBaseKit *)node;
2319     }
2320     if (path) path->append(kit);
2321     return SoBaseKit::findPart(newpartname, kit, partnum, islist,
2322                                listidx, makeifneeded, path, recsearch);
2323   }
2324 }
2325 
2326 //
2327 // makes part, makes sure node is connected in the scene
2328 //
2329 SbBool
makePart(const int partnum)2330 SoBaseKit::makePart(const int partnum)
2331 {
2332   assert(partnum > 0 && partnum < PRIVATE(this)->instancelist.getLength());
2333   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
2334   assert(catalog);
2335 
2336   SoNode * node = (SoNode *)catalog->getDefaultType(partnum).createInstance();
2337   if (catalog->isList(partnum)) {
2338     SoNodeKitListPart * list = (SoNodeKitListPart *) node;
2339     if (catalog->getListContainerType(partnum) != SoGroup::getClassTypeId()) {
2340       list->setContainerType(catalog->getListContainerType(partnum));
2341     }
2342     const SoTypeList & typelist = catalog->getListItemTypes(partnum);
2343     for (int i = 0; i < typelist.getLength(); i++) {
2344       list->addChildType(typelist[i]);
2345     }
2346     list->lockTypes();
2347   }
2348   return this->setPart(partnum, node);
2349 }
2350 
2351 /*!
2352   Sets parts, updates nodekit scene graph, and makes sure graph is
2353   valid with respect to right siblings and parent.  This method is
2354   virtual to enable subclasses to detect when a part changes value.
2355 
2356   This method is not part of the original SGI Open Inventor API, but
2357   is an extension specific to Coin.
2358 */
2359 SbBool
setPart(const int partnum,SoNode * node)2360 SoBaseKit::setPart(const int partnum, SoNode * node)
2361 {
2362   assert(partnum > 0 && partnum < PRIVATE(this)->instancelist.getLength());
2363   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
2364   assert(catalog);
2365 
2366   if (node && !node->getTypeId().isDerivedFrom(catalog->getType(partnum))) {
2367 #if COIN_DEBUG
2368     SoDebugError::postWarning("SoBaseKit::setPart",
2369                               "Attempted to set part ``%s'' "
2370                               "to wrong type. Expected ``%s'', got ``%s''",
2371                               catalog->getName(partnum).getString(),
2372                               catalog->getType(partnum).getName().getString(),
2373                               node->getTypeId().getName().getString());
2374 #endif // COIN_DEBUG
2375     return FALSE;
2376   }
2377   int parentIdx = catalog->getParentPartNumber(partnum);
2378   assert(parentIdx >= 0 && parentIdx < PRIVATE(this)->instancelist.getLength());
2379   SoNode * parent = NULL;
2380   if (parentIdx == 0) parent = this;
2381   else parent = PRIVATE(this)->instancelist[parentIdx]->getValue();
2382   if (parent == NULL) {
2383     this->makePart(parentIdx);
2384     parent = PRIVATE(this)->instancelist[parentIdx]->getValue();
2385   }
2386   assert(parent != NULL);
2387   SoChildList * childlist = parent->getChildren();
2388   assert(childlist != NULL);
2389 
2390   // if parent is a node derived from SoGroup, use the SoGroup access
2391   // functions to add/remove/insert children instead of SoChildList
2392   // directly. This is needed for VRML group nodes to work properly
2393   // inside node kits. pederb, 2004-06-23
2394   SoGroup * parentgroup = NULL;
2395   if (parent->isOfType(SoGroup::getClassTypeId())) {
2396     parentgroup = (SoGroup*) parent;
2397   }
2398 
2399   SoNode * oldnode = PRIVATE(this)->instancelist[partnum]->getValue();
2400   if (oldnode == node) return TRUE; // part is already inserted
2401 
2402   if (childlist->find(node) >= 0) {
2403     // FIXME: should really allow this, but since it's a bit complex
2404     // (we need to somehow keep better track of which SoGroup child
2405     // indices belong to which catalog parts), we just disallow it for
2406     // now. 20020808 mortene.
2407     SoDebugError::postWarning("SoBaseKit::setPart",
2408                               "Node pointer (%p, '%s', '%s') is already used under the same group node in the catalog "
2409                               "as a child of part '%s' -- this is not allowed",
2410                               node,
2411                               node->getName().getString(),
2412                               node->getTypeId().getName().getString(),
2413                               catalog->getName(parentIdx).getString());
2414     return FALSE;
2415   }
2416 
2417   if (oldnode != NULL) { // part exists, replace
2418     int oldIdx = childlist->find(oldnode);
2419     assert(oldIdx >= 0);
2420 
2421     if (parentgroup) {
2422       if (node) parentgroup->replaceChild(oldIdx, node);
2423       else parentgroup->removeChild(oldIdx);
2424     }
2425     else {
2426       if (node) childlist->set(oldIdx, node);
2427       else childlist->remove(oldIdx);
2428     }
2429   }
2430   else if (node) { // find where to insert in parent childlist
2431     int rightSibling = this->getRightSiblingIndex(partnum);
2432     if (rightSibling >= 0) { // part has right sibling, insert before
2433       int idx = childlist->find(PRIVATE(this)->instancelist[rightSibling]->getValue());
2434       assert(idx >= 0);
2435       if (parentgroup) {
2436         parentgroup->insertChild(node, idx);
2437       }
2438       else {
2439         childlist->insert(node, idx);
2440       }
2441     }
2442     else {
2443       if (parentgroup) {
2444         parentgroup->addChild(node);
2445       }
2446       else {
2447         childlist->append(node);
2448       }
2449     }
2450   }
2451 
2452   // set part field value
2453   PRIVATE(this)->instancelist[partnum]->setValue(node);
2454   return TRUE;
2455 }
2456 
2457 //
2458 // returns part number of existing right sibling or -1 if none exists
2459 //
2460 int
getRightSiblingIndex(const int partnum)2461 SoBaseKit::getRightSiblingIndex(const int partnum)
2462 {
2463   assert(partnum > 0 && partnum < PRIVATE(this)->instancelist.getLength());
2464   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
2465 
2466   int sibling = catalog->getRightSiblingPartNumber(partnum);
2467 
2468   // iterate until no more siblings or until we find an existing one
2469   while (sibling >= 0 && PRIVATE(this)->instancelist[sibling]->getValue() == NULL) {
2470     sibling = catalog->getRightSiblingPartNumber(sibling);
2471   }
2472   return sibling;
2473 }
2474 
2475 //
2476 // Searches the field list to find of a node is in this kit.
2477 // Returns catalog index, -1 if not found
2478 //
2479 // parentnum is checked if >= 0
2480 //
2481 int
findNodeInThisKit(SoNode * node,const int parentnum) const2482 SoBaseKit::findNodeInThisKit(SoNode * node, const int parentnum) const
2483 {
2484   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
2485   if (node == (SoNode *)this) return 0;
2486   int n = PRIVATE(this)->instancelist.getLength();
2487   for (int i = 1; i < n; i++) {
2488     if (PRIVATE(this)->instancelist[i]->getValue() == node &&
2489         (parentnum < 0 || catalog->getParentPartNumber(i) == parentnum))
2490       return i;
2491   }
2492   return -1;
2493 }
2494 
2495 // ******* methods in SoBaseKitP are below ******************************
2496 
2497 //
2498 // copy the fields in kit into a new fielddata. This is done to get
2499 // the correct write order: non-part fields first, then leaf parts,
2500 // then non-leaf parts.
2501 //
2502 void
createWriteData(void)2503 SoBaseKitP::createWriteData(void)
2504 {
2505   this->writedata = new SoFieldData;
2506   const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
2507   const SoFieldData * fielddata = kit->getFieldData();
2508 
2509   int n = fielddata->getNumFields();
2510   for (int pass = 0; pass < 3; pass++) {
2511     for (int i = 0; i < n; i++) {
2512       int part = catalog->getPartNumber(fielddata->getFieldName(i));
2513       // NB: earlier (before 2003-03-26) we did not write private
2514       // parts.  However, several users have reported that SGI/TGS
2515       // Inventor do this so we have to write them too.
2516       // pederb, 2003-03-26
2517       if ((pass == 0 && part < 0) ||
2518           (pass == 1 && part > 0 && catalog->isLeaf(part)) ||
2519           (pass == 2 && part > 0 && !catalog->isLeaf(part))) {
2520         this->writedata->addField(this->kit,
2521                                   fielddata->getFieldName(i).getString(),
2522                                   fielddata->getField(this->kit, i));
2523       }
2524     }
2525   }
2526 }
2527 
2528 //
2529 // test if parent part of a part is going to write, and if so
2530 // write part even if isDefault()
2531 //
2532 void
testParentWrite(void)2533 SoBaseKitP::testParentWrite(void)
2534 {
2535   const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
2536   int n = this->instancelist.getLength();
2537   for (int i = 1; i < n; i++) {
2538     SoSFNode * field = this->instancelist[i];
2539     if (field->isDefault()) { // we might not write
2540       SoNode * node = field->getValue();
2541       // don't write if NULL, of course
2542       if (node) {
2543         int parent = catalog->getParentPartNumber(i);
2544         if (parent > 0) {
2545           assert(this->writedata);
2546           SbName dummy;
2547           SoNode * parentnode = this->instancelist[parent]->getValue();
2548           // we must write if parent is going to write
2549           if (parentnode &&
2550               !this->instancelist[parent]->isDefault()) {
2551             field->setDefault(FALSE);
2552           }
2553         }
2554       }
2555     }
2556   }
2557 }
2558 
2559 // Copy parts into 'partlist'. All parts have already been copied, but
2560 // we need to update the parts that have a parent as a part, since the
2561 // part node has already been copied by the parent, and we need to use
2562 // that child node pointer, not the copied part.
2563 void
copyParts(const SoBaseKit * srckit,SbList<SoNode * > & partlist,const SbBool COIN_UNUSED_ARG (copyconnections))2564 SoBaseKitP::copyParts(const SoBaseKit * srckit, SbList <SoNode*> & partlist,
2565                       const SbBool COIN_UNUSED_ARG(copyconnections))
2566 {
2567   int i;
2568   const int n = this->instancelist.getLength();
2569   const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
2570 
2571   // convenient reference
2572   const SbList <SoSFNode*> & srcfields = srckit->getCatalogInstances();
2573 
2574   // copy parts that do not have a parent as a part
2575   for (i = 1; i < n; i++) {
2576     SoNode * dstnode = this->instancelist[i]->getValue();
2577     if (dstnode && catalog->getParentPartNumber(i) == 0) {
2578       SoNode * srcnode = srcfields[i]->getValue();
2579       assert(dstnode != srcnode);
2580       assert(srcnode != NULL);
2581       assert(srcnode->getTypeId() == dstnode->getTypeId());
2582       srcnode->assertAlive();
2583       dstnode->assertAlive();
2584       // the node has been copied since we called
2585       // SoNode::copyContents() . We just need to store the pointer
2586       dstnode->ref(); // ref before inserting into list
2587       if (partlist[i]) partlist[i]->unref();
2588       partlist[i] = dstnode;
2589     }
2590   }
2591   // copy parts where parent is a part. These parts will already
2592   // have been copied, but we need to figure out the parent part node,
2593   // and use the correct child node as the part node instead of the
2594   // already copied part node.
2595   for (i = 1; i < n; i++) {
2596     int parent = catalog->getParentPartNumber(i);
2597     if (parent > 0 && this->instancelist[i]->getValue()) {
2598       SoNode * srcgroup = srcfields[parent]->getValue();
2599       assert(srcgroup);
2600       SoNode * dstgroup = partlist[parent];
2601       assert(dstgroup);
2602       assert(dstgroup->getChildren());
2603       assert(srcgroup->getChildren());
2604 
2605       // find child index in src kit
2606       int childidx = srcgroup->getChildren()->find(srcfields[i]->getValue());
2607       assert(childidx >= 0);
2608 
2609       // use the already copied child as part node
2610       assert(childidx < dstgroup->getChildren()->getLength());
2611       SoNode * child = (*(dstgroup->getChildren()))[childidx];
2612       child->ref(); // ref before inserting
2613       if (partlist[i]) partlist[i]->unref(); // unref old node in list
2614       partlist[i] = child;
2615     }
2616   }
2617 }
2618 
2619 void
setParts(SbList<SoNode * > partlist,const SbBool leafparts)2620 SoBaseKitP::setParts(SbList <SoNode*> partlist, const SbBool leafparts)
2621 {
2622   const int n = this->instancelist.getLength();
2623   const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
2624 
2625   for (int i = 1; i < n; i++) {
2626     SoNode * node = partlist[i];
2627     if (node) {
2628       SbBool leaftst = catalog->isLeaf(i);
2629       if (leaftst == leafparts) { // correct pass ?
2630         if (!leaftst) {
2631           // if it's not a leaf, remove children as the correct children
2632           // will be added  when children parts are set.
2633           assert(node->getChildren());
2634           node->getChildren()->truncate(0);
2635         }
2636         this->kit->setPart(i, node);
2637       }
2638     }
2639   }
2640 }
2641 
2642 //
2643 // Adds a SoNodekitDetail to the picked point. path should
2644 // contain this kit.
2645 //
2646 void
addKitDetail(SoFullPath * path,SoPickedPoint * pp)2647 SoBaseKitP::addKitDetail(SoFullPath * path, SoPickedPoint * pp)
2648 {
2649   const SoNodekitCatalog * catalog = this->kit->getNodekitCatalog();
2650 
2651   assert(path->findNode(this->kit) >= 0);
2652 
2653   const int n = path->getLength();
2654   for (int i = path->findNode(this->kit) + 1; i < n; i++) {
2655     SoNode * node = path->getNode(i);
2656     int idx = this->kit->findNodeInThisKit(node, -1);
2657     if (idx > 0 && catalog->isLeaf(idx)) {
2658       SoNodeKitDetail * detail = new SoNodeKitDetail;
2659       detail->setNodeKit(this->kit);
2660       detail->setPart(node);
2661       SbString partname(catalog->getName(idx));
2662       // check if node is a SoNodeKitListPart, and if the
2663       // path extends into the children. Supply index in partname
2664       // if this is the case.
2665       if (node->isOfType(SoNodeKitListPart::getClassTypeId()) &&
2666           path->getLength() >= i + 2) {
2667         SbString str;
2668         str.sprintf("%s[%d]",
2669                     partname.getString(),
2670                     path->getIndex(i+2));
2671         partname = SbName(str.getString());
2672       }
2673       detail->setPartName(partname);
2674       pp->setDetail(detail, this->kit);
2675       // finished
2676       break;
2677     }
2678   }
2679 }
2680 
2681 //  Reading in parts of nested nodekits does not allow certain shortcuts
2682 //  that are specified by the Inventor Mentor. The Mentor specifies that
2683 //  within nested nodekits intermediary kits can be left out and will be
2684 //  created automatically. Reported by Gerhard Reitmayr.
2685 SbBool
readUnknownFields(SoInput * in,SoFieldData * & unknownfielddata)2686 SoBaseKitP::readUnknownFields(SoInput *in, SoFieldData *&unknownfielddata)
2687 {
2688   const SoFieldData * fd = PUBLIC(this)->getFieldData();
2689 
2690   // Binary format
2691   if (in->isBinary()) {
2692     SbBool notbuiltin;
2693     return fd->read(in, PUBLIC(this), TRUE, notbuiltin);
2694   }
2695 
2696   SbBool firstfield = TRUE;
2697   SbName fielddescriptionsmarker("fields");
2698 
2699   // ASCII format
2700   // keep reading fields until we hit close bracket
2701   while (TRUE) {
2702     // read first character - if none, EOF
2703     char c;
2704     if (!in->read(c))
2705       return FALSE;
2706     in->putBack(c);
2707 
2708     if (c == '}')
2709       return TRUE;
2710 
2711     // read fieldname with no identifier, to be able to read names like
2712     // appearance.material
2713     SbName fieldname;
2714     if (!in->read(fieldname, FALSE))
2715       return TRUE;
2716 
2717     // if this is the first field we try to read, it might be the
2718     // field descriptions for extension node kits. Detect and read.
2719     if (firstfield) {
2720       firstfield = FALSE;
2721       if (fieldname == fielddescriptionsmarker) {
2722         if (!fd->readFieldDescriptions(in, PUBLIC(this), 0, FALSE)) {
2723           return FALSE;
2724         }
2725         continue; // read next field
2726       }
2727     }
2728 
2729     // try to read data into one of the fields in this nodekit first.
2730     // SoFieldData::read() will return TRUE and set foundname to FALSE
2731     // if the field isn't part of the node(kit)
2732     SbBool foundname;
2733     if (!fd->read(in, PUBLIC(this), fieldname, foundname))
2734       return FALSE;
2735 
2736     if (!foundname) {
2737       // add a node pointer field with this name to the unknownFieldData,
2738       // and read it
2739       unknownfielddata->addField(PUBLIC(this), fieldname.getString(),
2740                                  new SoSFNode);
2741       if (!unknownfielddata->read(in, PUBLIC(this), fieldname, foundname))
2742         return FALSE;
2743     }
2744   }
2745   // Will never be reached, but functions with a return value other than
2746   // void must return *something* by default. At least gcc-4.0.0
2747   // (Apple snapshot 20041026, default in Mac OS 10.4) will warn.
2748   return TRUE;
2749 }
2750 
2751 #undef PRIVATE
2752 #undef PUBLIC
2753 
2754 #endif // HAVE_NODEKITS
2755