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