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 /*!
34   \class SoGroup SoGroup.h Inventor/nodes/SoGroup.h
35   \brief The SoGroup class is a node which managed other node instances.
36 
37   \ingroup nodes
38 
39   The internal scene data structures in Coin are managed as directed
40   graphs. The graphs are built by setting up a hierarchy through the
41   use of group nodes (either of this type, or from subclasses like
42   SoSeparator) which is then traversed when applying actions (like
43   SoGLRenderAction) to it.
44 
45   SoGroup, SoSeparator, and other classes derived from SoGroup, are
46   the "tools" the application programmer uses when making the layout
47   of the scene graph.
48 
49   An often asked question about SoGroup nodes is: "Why is there no
50   SoGroup::getParent() method?" The answer to this is that nodes in
51   the scene graph can have multiple parents, so a simple getParent()
52   method wouldn't work. If you have a node pointer (or other node
53   identification) of a node that you want to remove from the scene
54   graph, what you need to do is to use an SoSearchAction to find all
55   paths down to the node, and then invoke SoGroup::removeChild() from
56   all found parents of the node.
57 
58   The function would look something like this:
59 
60   \code
61   void getParents(SoNode * node, SoNode * root, SoPathList & parents)
62   {
63     SoSearchAction sa;
64     sa.setNode(node);
65     sa.setInterest(SoSearchAction::ALL);
66     sa.apply(root);
67     parents = sa.getPaths();
68   }
69   \endcode
70 
71   Or if you \e know that your node of interest has only a single
72   parent:
73 
74   \code
75   SoGroup * getParent(SoNode * node, SoNode * root)
76   {
77     SoSearchAction sa;
78     sa.setNode(node);
79     sa.setInterest(SoSearchAction::FIRST);
80     sa.apply(root);
81     SoPath * p = sa.getPath();
82     assert(p && "not found");
83     if (p->getLength() < 2) { return NULL; } // no parent
84     return (SoGroup *)p->getNodeFromTail(1);
85   }
86   \endcode
87 
88 
89   An important note about a potential problem using SoGroup nodes
90   which it is not common to stumble on, but which makes hard to find
91   bugs when one does: you should not change the scene graph layout
92   during any action traversal, as that is not allowed by the internal
93   Coin code. I.e. do not use addChild(), removeChild(), insertChild()
94   or replaceChild() from any callback that is triggered directly or
95   indirectly from an action traversal. The most common way of getting
96   hit by this error, would be something like the following
97   (simplified) example:
98 
99   \code
100   #include <Inventor/Qt/SoQt.h>
101   #include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
102 
103   #include <Inventor/nodes/SoEventCallback.h>
104   #include <Inventor/nodes/SoSeparator.h>
105   #include <Inventor/nodes/SoCone.h>
106   #include <Inventor/manips/SoPointLightManip.h>
107   #include <Inventor/events/SoMouseButtonEvent.h>
108 
109   SoPointLightManip * global_pointlightmanip;
110   SoSeparator * global_root;
111 
112   // Remove pointlight when clicking right mouse button.
113   static void
114   mySelectionC(void * ud, SoEventCallback * n)
115   {
116     const SoMouseButtonEvent * mbe = (SoMouseButtonEvent*) n->getEvent();
117 
118     if ((mbe->getButton() == SoMouseButtonEvent::BUTTON2) &&
119 	(mbe->getState() == SoButtonEvent::DOWN)) {
120       if (global_pointlightmanip) {
121 	global_root->removeChild(global_pointlightmanip);
122 	global_pointlightmanip = NULL;
123       }
124     }
125   }
126 
127   int
128   main(int argc, char ** argv)
129   {
130     QWidget * window = SoQt::init(argv[0]);
131 
132     global_root = new SoSeparator;
133     global_root->ref();
134 
135     SoEventCallback * ecb = new SoEventCallback;
136     ecb->addEventCallback(SoMouseButtonEvent::getClassTypeId(), mySelectionC, 0);
137     global_root->addChild(ecb);
138 
139     global_root->addChild(new SoCone);
140 
141     global_pointlightmanip = new SoPointLightManip;
142     global_root->addChild(global_pointlightmanip);
143 
144     SoQtExaminerViewer * viewer = new SoQtExaminerViewer(window);
145     viewer->setSceneGraph(global_root);
146     viewer->show();
147 
148     SoQt::show(window);
149     SoQt::mainLoop();
150 
151     global_root->unref();
152     delete viewer;
153 
154     return 0;
155   }
156   \endcode
157 
158   What happens in the above case is this: when clicking with the right
159   mouse button, the SoQtExaminerViewer converts the Qt event to a Coin
160   event, which is sent down the scene graph with an
161   SoHandleEventAction. The action traversal reaches the "global_root"
162   SoSeparator node, where it sees that it should further traverse 3
163   child nodes (first the SoEventCallback, then the SoCone, then the
164   SoPointLightManip). When it then traverses the SoEventCallback, the
165   mySelectionC() callback will be invoked, which removes the last
166   child. But the SoHandleEventAction will still continue it's
167   traversal as if the global_root node has 3 children -- and the code
168   will crash.
169 
170   (This exact example would perhaps be straight-forward to handle
171   internally in Coin, but there are other ways to change the scene
172   graph layout that are very difficult to handle properly. So in
173   general, changing layout during action traversal is not allowed.)
174 
175   What to do in these cases is to change the code inside the callback
176   to not do any operations that immediately changes the layout of the
177   scene graph, but to delay it for after the traversal is done. This
178   can e.g. be done by using a Coin sensor.
179 
180   <b>FILE FORMAT/DEFAULTS:</b>
181   \code
182     Group {
183     }
184   \endcode
185 */
186 
187 // *************************************************************************
188 
189 #include <Inventor/nodes/SoGroup.h>
190 
191 #ifdef HAVE_CONFIG_H
192 #include "config.h"
193 #endif // HAVE_CONFIG_H
194 
195 #include <cassert>
196 
197 #include <Inventor/SoInput.h>
198 #include <Inventor/SoOutput.h>
199 #include <Inventor/misc/SoChildList.h>
200 #include <Inventor/actions/SoGetBoundingBoxAction.h>
201 #include <Inventor/actions/SoGetMatrixAction.h>
202 #include <Inventor/actions/SoGLRenderAction.h>
203 #include <Inventor/actions/SoSearchAction.h>
204 #include <Inventor/actions/SoWriteAction.h>
205 #include <Inventor/actions/SoAudioRenderAction.h>
206 #include <Inventor/errors/SoReadError.h>
207 #include <Inventor/actions/SoCallbackAction.h>
208 #include <Inventor/elements/SoCacheElement.h>
209 #include <Inventor/errors/SoDebugError.h>
210 #include <Inventor/system/gl.h>
211 
212 #include "nodes/SoSubNodeP.h"
213 #include "rendering/SoGL.h"
214 #include "glue/glp.h"
215 #include "io/SoWriterefCounter.h"
216 
217 #include <Inventor/annex/Profiler/SoProfiler.h>
218 #include "profiler/SoNodeProfiling.h"
219 
220 // *************************************************************************
221 
222 /*!
223   \var SoChildList * SoGroup::children
224   List of managed child nodes.
225 */
226 
227 // *************************************************************************
228 // Note: just static data here, as there's no Cheshire Cat pattern (ie
229 // pimpl-ptr) implemented for SoNode. (The class should be as slim as
230 // possible.)
231 
232 class SoGroupP {
233 public:
234   typedef void GLRenderFunc(SoGroup *, SoNode *, SoGLRenderAction *);
235   static GLRenderFunc * glrenderfunc;
236   static void childGLRender(SoGroup * thisp, SoNode * child, SoGLRenderAction * action);
237   static void childGLRenderProfiler(SoGroup * thisp, SoNode * child, SoGLRenderAction * action);
238 };
239 
240 SoGroupP::GLRenderFunc * SoGroupP::glrenderfunc = NULL;
241 
242 // *************************************************************************
243 
244 SO_NODE_SOURCE(SoGroup);
245 
246 // *************************************************************************
247 
248 /*!
249   Default constructor.
250 */
SoGroup(void)251 SoGroup::SoGroup(void)
252 {
253   this->pimpl = NULL; // just set to NULL for now
254   SO_NODE_INTERNAL_CONSTRUCTOR(SoGroup);
255 
256   this->children = new SoChildList(this);
257   this->setOperation();
258 }
259 
260 /*!
261   Constructor.
262 
263   The argument should be the approximate number of children which is
264   expected to be inserted below this node. The number need not be
265   exact, as it is only used as a hint for better memory resource
266   allocation.
267 */
SoGroup(int nchildren)268 SoGroup::SoGroup(int nchildren)
269 {
270   SO_NODE_INTERNAL_CONSTRUCTOR(SoGroup);
271 
272   this->children = new SoChildList(this, nchildren);
273   this->setOperation();
274 }
275 
276 /*!
277   Destructor.
278 */
~SoGroup()279 SoGroup::~SoGroup()
280 {
281   delete this->children;
282 }
283 
284 /*!
285   Returns pointer to child node at \a index.
286 
287   Please note that this method is not virtual in the original SGI
288   Inventor API.
289 */
290 SoNode *
getChild(int index) const291 SoGroup::getChild(int index) const
292 {
293   assert((index >= 0) && (index < this->getNumChildren()));
294 
295   return (SoNode*) this->getChildren()->getArrayPtr()[index];
296 }
297 
298 /*!
299   Returns number of child nodes managed by this group.
300 
301   Please note that this method is not virtual in the original SGI
302   Inventor API.
303 */
304 int
getNumChildren(void) const305 SoGroup::getNumChildren(void) const
306 {
307   return this->getChildren()->getLength();
308 }
309 
310 // Doc from superclass.
311 SbBool
readInstance(SoInput * in,unsigned short flags)312 SoGroup::readInstance(SoInput * in, unsigned short flags)
313 {
314   SbBool readfields = TRUE;
315 
316   // Make sure we're compatible with binary format Inventor 1.0 and
317   // 2.0 files.
318   if (in->isBinary() && (in->getIVVersion() < 2.1f) &&
319       this->getTypeId() == SoGroup::getClassTypeId()) {
320     readfields = FALSE;
321   }
322 
323   // Make sure we're compatible with binary format Inventor 1.0 files.
324   if (in->isBinary() && (in->getIVVersion() < 2.0f) &&
325       // For Inventor V1.0 files, no fields should be attempted read
326       // from SoSeparator nodes when reading from binary format files,
327       // or the input parsing will go wrong. I have just assumed this
328       // goes for all SoGroup-derived nodes -- which may not be
329       // correct. 20050706 mortene.
330       this->isOfType(SoGroup::getClassTypeId())) {
331     readfields = FALSE;
332   }
333 
334   // This influences how SoFieldContainer::readInstance() handles
335   // unknown field names inside the node: if it's a group, ignore, as
336   // it can be the name of a node type.
337   flags |= SoBase::IS_GROUP;
338 
339   // For nodes with fields inheriting SoGroup, the fields must come
340   // before the children, according to the file format specification.
341   if (readfields && !inherited::readInstance(in, flags)) return FALSE;
342 
343   return this->readChildren(in);
344 }
345 
346 /*!
347   Read all children of this node from \a in and attach them below this
348   group in left-to-right order. Returns \c FALSE upon read error.
349 */
350 SbBool
readChildren(SoInput * in)351 SoGroup::readChildren(SoInput * in)
352 {
353   unsigned int numchildren = 0; // used by binary format import
354   if (in->isBinary() && !in->read(numchildren)) {
355     SoReadError::post(in, "Premature end of file");
356     return FALSE;
357   }
358 
359   for (unsigned int i=0; !in->isBinary() || (i < numchildren); i++) {
360     SoBase * child;
361     if (SoBase::read(in, child, SoNode::getClassTypeId())) {
362       if (child == NULL) {
363 	if (in->eof()) {
364 	  SoReadError::post(in, "Premature end of file");
365 	  return FALSE;
366 	}
367 	else {
368 	  if (in->isBinary()) {
369 	    SoReadError::post(in, "Couldn't read valid identifier name");
370 	    return FALSE;
371 	  }
372 
373 #if COIN_DEBUG && 0 // debug
374 	  char m;
375 	  if (in->read(m)) {
376 	    SoDebugError::postInfo("SoGroup::readChildren",
377 				   "next char: '%c'", m);
378 	  }
379 #endif // debug
380 	  // Completed reading of children for ASCII format import.
381 	  return TRUE;
382 	}
383       }
384       else {
385 	this->addChild((SoNode *)child);
386       }
387     }
388     else {
389       // SoReadError::post() is called within the SoBase::read()
390       // frame upon error conditions, so don't duplicate with
391       // another error message here.  mortene.
392       return FALSE;
393     }
394   }
395 
396   // A successful import operation for binary format reading of child
397   // nodes will exit here.
398   return TRUE;
399 }
400 
401 // Overridden from parent.
402 void
copyContents(const SoFieldContainer * from,SbBool copyconnections)403 SoGroup::copyContents(const SoFieldContainer * from, SbBool copyconnections)
404 {
405   this->removeAllChildren();
406 
407   inherited::copyContents(from, copyconnections);
408 
409   SoGroup * g = (SoGroup *)from;
410 
411   // Add children of "from" group node.
412   for (int i=0 ; i < g->getNumChildren(); i++) {
413     SoNode * cp = (SoNode *)
414       SoFieldContainer::findCopy(g->getChild(i), copyconnections);
415     this->addChild(cp);
416   }
417 }
418 
419 SoNotRec
createNotRec(void)420 SoGroup::createNotRec(void)
421 {
422   SoNotRec rec(inherited::createNotRec());
423   rec.setOperationType(operationType);
424   rec.setGroupChild(changedChild);
425   rec.setGroupPrevChild(changedPrevChild);
426   rec.setIndex(changedIndex);
427   return rec;
428 }
429 
430 /*!
431   \internal
432 */
433 void
setOperation(const SoNotRec::OperationType opType,const SoNode * nc,const SoNode * pc,const int ci)434 SoGroup::setOperation(const SoNotRec::OperationType opType,
435 		      const SoNode * nc,
436 		      const SoNode * pc,
437 		      const int ci)
438 {
439   this->operationType = opType;
440   this->changedChild = nc;
441   this->changedPrevChild = pc;
442   this->changedIndex = ci;
443 }
444 
445 /*!
446   Append a child \a node to the list of children nodes this group node
447   is managing.
448 
449   Please note that this method is not virtual in the original SGI
450   Inventor API.
451 */
452 void
addChild(SoNode * node)453 SoGroup::addChild(SoNode * node)
454 {
455   assert(node != NULL);
456   this->setOperation(SoNotRec::GROUP_ADDCHILD, node);
457   this->getChildren()->append(node);
458   this->setOperation();
459 }
460 
461 /*!
462   Insert a \a child node at position \a newchildindex.
463 
464   \a newchildindex must be <= this->getNumChildren()
465 
466   Please note that this method is not virtual in the original SGI
467   Inventor API.
468 */
469 void
insertChild(SoNode * child,int newchildindex)470 SoGroup::insertChild(SoNode * child, int newchildindex)
471 {
472 #if COIN_DEBUG
473   if (newchildindex < 0 || newchildindex > this->getNumChildren()) {
474     SoDebugError::post("SoGroup::insertChild",
475 		       "idx %d is out of bounds (groupnode # children == %d)",
476 		       newchildindex, this->getNumChildren());
477     return;
478   }
479 #endif // COIN_DEBUG
480   this->setOperation(SoNotRec::GROUP_INSERTCHILD, child, NULL, newchildindex);
481   this->getChildren()->insert(child, newchildindex);
482   this->setOperation();
483 }
484 
485 /*!
486   Remove node at \a childindex in our list of children.
487 
488   Please note that this method is not virtual in the original SGI
489   Inventor API.
490 */
491 void
removeChild(int childindex)492 SoGroup::removeChild(int childindex)
493 {
494 #if COIN_DEBUG
495   if (childindex < 0 || childindex >= this->getNumChildren()) {
496     SoDebugError::post("SoGroup::removeChild",
497 		       "idx %d is out of bounds (groupnode # children == %d)",
498 		       childindex, this->getNumChildren());
499     return;
500   }
501 #endif // COIN_DEBUG
502   this->setOperation(SoNotRec::GROUP_REMOVECHILD,
503 		     this->getChild(childindex),
504 		     NULL, childindex);
505   this->getChildren()->remove(childindex);
506   this->setOperation();
507 }
508 
509 /*!
510   Returns index in our list of children for child \a node, or -1 if \a
511   node is not a child of this group node.
512 
513   Please note that this method is not virtual in the original SGI
514   Inventor API.
515 */
516 int
findChild(const SoNode * node) const517 SoGroup::findChild(const SoNode * node) const
518 {
519   return this->getChildren()->find((SoNode *) node);
520 }
521 
522 // Doc from superclass.
523 void
initClass(void)524 SoGroup::initClass(void)
525 {
526   SO_NODE_INTERNAL_INIT_CLASS(SoGroup, SO_FROM_INVENTOR_1);
527 
528   // for the built-in Coin profiler. set up the functionptr to use, so
529   // we don't have any overhead when profiling is off:
530   SoGroupP::glrenderfunc = SoGroupP::childGLRender;
531   if (SoProfiler::isEnabled()) {
532     SoGroupP::glrenderfunc = SoGroupP::childGLRenderProfiler;
533   }
534 }
535 
536 // *************************************************************************
537 
538 // Doc from superclass.
539 void
doAction(SoAction * action)540 SoGroup::doAction(SoAction * action)
541 {
542   int numindices;
543   const int * indices;
544   if (action->getPathCode(numindices, indices) == SoAction::IN_PATH) {
545     this->getChildren()->traverseInPath(action, numindices, indices);
546   }
547   else {
548     this->getChildren()->traverse(action); // traverse all children
549   }
550 }
551 
552 // *************************************************************************
553 
554 // Doc from superclass.
555 void
getBoundingBox(SoGetBoundingBoxAction * action)556 SoGroup::getBoundingBox(SoGetBoundingBoxAction * action)
557 {
558   // Sanity check. This has caught bugs.
559   assert(this->getNumChildren() == this->getChildren()->getLength());
560 
561   int numindices;
562   const int * indices;
563   int lastchildindex;
564 
565   if (action->getPathCode(numindices, indices) == SoAction::IN_PATH)
566     lastchildindex = indices[numindices-1];
567   else
568     lastchildindex = this->getNumChildren() - 1;
569 
570   assert(lastchildindex < this->getNumChildren());
571 
572   // Initialize accumulation variables.
573   SbVec3f acccenter(0.0f, 0.0f, 0.0f);
574   int numcenters = 0;
575 
576   for (int i = 0; i <= lastchildindex; i++) {
577     this->getChildren()->traverse(action, i);
578 
579     // If center point is set, accumulate.
580     if (action->isCenterSet()) {
581       acccenter += action->getCenter();
582 	numcenters++;
583 	action->resetCenter();
584     }
585   }
586 
587   if (numcenters != 0)
588     action->setCenter(acccenter / float(numcenters), FALSE);
589 }
590 
591 // *************************************************************************
592 
593 void
childGLRender(SoGroup * COIN_UNUSED_ARG (thisp),SoNode * child,SoGLRenderAction * action)594 SoGroupP::childGLRender(SoGroup * COIN_UNUSED_ARG(thisp), SoNode * child, SoGLRenderAction * action)
595 {
596   child->GLRender(action);
597 }
598 
599 // This function is called for each child to traverse, and
600 // action->getCurPath() is already updated at this point.
601 void
childGLRenderProfiler(SoGroup * COIN_UNUSED_ARG (thisp),SoNode * child,SoGLRenderAction * action)602 SoGroupP::childGLRenderProfiler(SoGroup * COIN_UNUSED_ARG(thisp), SoNode * child, SoGLRenderAction * action)
603 {
604   SoNodeProfiling profiling;
605   profiling.preTraversal(action);
606   child->GLRender(action);
607   profiling.postTraversal(action);
608 }
609 
610 // Doc from superclass.
611 void
GLRender(SoGLRenderAction * action)612 SoGroup::GLRender(SoGLRenderAction * action)
613 {
614   int numindices;
615   const int * indices;
616   SoAction::PathCode pathcode = action->getPathCode(numindices, indices);
617 
618   SoNode ** childarray = (SoNode**) this->getChildren()->getArrayPtr();
619   SoState * state = action->getState();
620 
621   if (pathcode == SoAction::IN_PATH) {
622     int lastchild = indices[numindices - 1];
623     for (int i = 0; i <= lastchild && !action->hasTerminated(); i++) {
624       SoNode * child = childarray[i];
625 
626       action->pushCurPath(i, child);
627       if (action->getCurPathCode() != SoAction::OFF_PATH ||
628 	  child->affectsState()) {
629 	if (!action->abortNow()) {
630 	  (*SoGroupP::glrenderfunc)(this, child, action);
631 	}
632 	else {
633 	  SoCacheElement::invalidate(state);
634 	}
635       }
636       action->popCurPath(pathcode);
637     }
638   }
639   else {
640     action->pushCurPath();
641     int n = this->getChildren()->getLength();
642     for (int i = 0; i < n && !action->hasTerminated(); i++) {
643       action->popPushCurPath(i, childarray[i]);
644 
645       if (pathcode == SoAction::OFF_PATH && !childarray[i]->affectsState()) {
646 	continue;
647       }
648 
649       if (action->abortNow()) {
650 	// only cache if we do a full traversal
651 	SoCacheElement::invalidate(state);
652 	break;
653       }
654 
655       (*SoGroupP::glrenderfunc)(this, childarray[i], action);
656 
657 #if COIN_DEBUG
658       // The GL error test is default disabled for this optimized
659       // path.  If you get a GL error reporting an error in the
660       // Separator node, enable this code by setting the environment
661       // variable COIN_GLERROR_DEBUGGING to "1" to see exactly which
662       // node caused the error.
663       static SbBool chkglerr = sogl_glerror_debugging();
664       if (chkglerr) {
665 	cc_string str;
666 	cc_string_construct(&str);
667 	const unsigned int errs = coin_catch_gl_errors(&str);
668 	if (errs > 0) {
669 	  SoDebugError::post("SoGroup::GLRender",
670 			     "glGetError()s => '%s', nodetype: '%s'",
671 			     cc_string_get_text(&str),
672 			     (*this->getChildren())[i]->getTypeId().getName().getString());
673 	}
674 	cc_string_clean(&str);
675       }
676 #endif // COIN_DEBUG
677 
678     }
679     action->popCurPath();
680   }
681 }
682 
683 // *************************************************************************
684 
685 // Doc from superclass.
686 void
callback(SoCallbackAction * action)687 SoGroup::callback(SoCallbackAction * action)
688 {
689   SoGroup::doAction((SoAction *)action);
690 }
691 
692 // Doc from superclass.
693 void
getMatrix(SoGetMatrixAction * action)694 SoGroup::getMatrix(SoGetMatrixAction * action)
695 {
696   switch (action->getCurPathCode()) {
697   case SoAction::NO_PATH:
698   case SoAction::BELOW_PATH:
699     break;
700   case SoAction::OFF_PATH:
701   case SoAction::IN_PATH:
702     SoGroup::doAction((SoAction *)action);
703     break;
704   }
705 }
706 
707 // Doc from superclass.
708 void
pick(SoPickAction * action)709 SoGroup::pick(SoPickAction * action)
710 {
711   SoGroup::doAction((SoAction *)action);
712 }
713 
714 // Doc from superclass.
715 void
handleEvent(SoHandleEventAction * action)716 SoGroup::handleEvent(SoHandleEventAction * action)
717 {
718   SoGroup::doAction((SoAction *)action);
719 }
720 
721 // Doc from superclass
722 void
audioRender(SoAudioRenderAction * action)723 SoGroup::audioRender(SoAudioRenderAction * action)
724 {
725   SoGroup::doAction(action);
726 }
727 
728 // Doc from superclass.
729 void
addWriteReference(SoOutput * out,SbBool isfromfield)730 SoGroup::addWriteReference(SoOutput * out, SbBool isfromfield)
731 {
732   // SoGroup::write() used to count write references of children by calling
733   // doAction() when ref was zero in the SoOutput::COUNT_REFS stage. However,
734   // also field connections may add write references without going through
735   // SoGroup::write(), see SoField::countWriteRefs(). This resulted in wrong
736   // write reference counts. Therefore addWriteReference() was overloaded to
737   // correctly count the write references of children regardless from where
738   // this is called.
739 
740   int ref = SoWriterefCounter::instance(out)->getWriteref(this);
741   inherited::addWriteReference(out);
742 
743   // Traverse hierarchy only first time around
744   if (ref == 0) {
745     int n = this->getChildren()->getLength();
746     for (int i = 0; i < n; i++) {
747 	  (*this->getChildren())[i]->addWriteReference(out);
748     }
749   }
750 }
751 
752 // Doc from superclass.
753 void
write(SoWriteAction * action)754 SoGroup::write(SoWriteAction * action)
755 {
756   SoOutput * out = action->getOutput();
757   if (out->getStage() == SoOutput::COUNT_REFS) {
758     this->addWriteReference(out);
759   }
760   else if (out->getStage() == SoOutput::WRITE) {
761     if (this->writeHeader(out, TRUE, FALSE)) return;
762     this->getFieldData()->write(out, this);
763     if (out->isBinary()) out->write(this->getNumChildren());
764     SoGroup::doAction((SoAction *)action);
765     this->writeFooter(out);
766   }
767   else assert(0 && "unknown stage");
768 }
769 
770 // Doc from superclass.
771 void
search(SoSearchAction * action)772 SoGroup::search(SoSearchAction * action)
773 {
774   // Include this node in the search.
775   inherited::search(action);
776   if (action->isFound()) return;
777 
778   // If we're not the one being sought after, try child subgraphs.
779   SoGroup::doAction((SoAction *)action);
780 }
781 
782 /*!
783   Returns list of children.
784 */
785 SoChildList *
getChildren(void) const786 SoGroup::getChildren(void) const
787 {
788   return ((SoGroup *)this)->children;
789 }
790 
791 /*!
792   Remove \a child from the set of children managed by this group node.
793   Will decrease the reference count of \a child by 1.
794 
795   This is a convenience method. It will simply call findChild() with
796   \a child as argument, and then call removeChild(int) if the child is
797   found.
798 
799   Please note that this method is not virtual in the original SGI
800   Inventor API.
801 */
802 void
removeChild(SoNode * child)803 SoGroup::removeChild(SoNode * child)
804 {
805   int idx = this->findChild(child);
806 
807   if (idx < 0) {
808 #if COIN_DEBUG
809     SoDebugError::post("SoGroup::removeChild",
810 		       "tried to remove non-existent child %p (%s)",
811 		       child,
812 		       child ? child->getTypeId().getName().getString() : "");
813 #endif // COIN_DEBUG
814     return;
815   }
816 
817   this->removeChild(idx);
818 }
819 
820 /*!
821   Do not manage the children anymore. Will dereference all children by
822   1 as they are removed.
823 
824   Please note that this method is not virtual in the original SGI
825   Inventor API.
826 */
827 void
removeAllChildren(void)828 SoGroup::removeAllChildren(void)
829 {
830   this->setOperation(SoNotRec::GROUP_REMOVEALLCHILDREN);
831   this->getChildren()->truncate(0);
832   this->setOperation();
833 }
834 
835 /*!
836   Replace child at \a index with \a newChild.
837 
838   Dereferences the child previously at \a index, and increases the
839   reference count of \a newChild by 1.
840 
841   \a index must be < this->getNumChildren()
842 
843   Please note that this method is not virtual in the original SGI
844   Inventor API.
845 */
846 void
replaceChild(int index,SoNode * newchild)847 SoGroup::replaceChild(int index, SoNode * newchild)
848 {
849   // Note: its imperative that we use set() here, and not a
850   // remove+insert pair of calls as that would puck up SoChildList
851   // auditing from SoPath instances.
852   this->setOperation(SoNotRec::GROUP_REPLACECHILD, newchild,
853 		     this->getChild(index), index);
854   this->getChildren()->set(index, newchild);
855   this->setOperation();
856 }
857 
858 /*!
859   Replace \a oldchild with \a newchild.
860 
861   Dereferences \a oldchild by 1, and increases the reference count of
862   \a newchild by 1.
863 
864   This is a convenience method. It will simply call findChild() with
865   \a oldchild as argument, and call replaceChild(int, SoNode*) if the
866   child is found.
867 
868   Please note that this method is not virtual in the original SGI
869   Inventor API.
870 */
871 void
replaceChild(SoNode * oldchild,SoNode * newchild)872 SoGroup::replaceChild(SoNode * oldchild, SoNode * newchild)
873 {
874 #if COIN_DEBUG && 0 // debug
875   SoDebugError::postInfo("SoGroup::replaceChild",
876 			 "(%p) from %p (%s) to %p (%s)",
877 			 this,
878 			 oldchild,
879 			 oldchild->getTypeId().getName().getString(),
880 			 newchild,
881 			 newchild->getTypeId().getName().getString());
882 #endif // debug
883 
884   int idx = this->findChild(oldchild);
885 
886 #if COIN_DEBUG
887   if (idx < 0 || idx > this->getNumChildren()) {
888     SoDebugError::post("SoGroup::replaceChild",
889 		       "(%p) Tried to remove non-existent child %p (%s)",
890 		       this,
891 		       oldchild,
892 		       oldchild->getTypeId().getName().getString());
893     return;
894   }
895 #endif // COIN_DEBUG
896 
897   this->replaceChild(idx, newchild);
898 }
899 
900 // Doc from parent class.
901 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)902 SoGroup::getPrimitiveCount(SoGetPrimitiveCountAction * action)
903 {
904   SoGroup::doAction((SoAction *)action);
905 }
906