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