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 SoInteractionKit SoInteractionKit.h Inventor/nodekits/SoInteractionKit.h
41   \brief The SoInteractionKit class is a base class for draggers.
42 
43   \ingroup nodekits
44 
45   This nodekit class makes it possible to set surrogate paths for
46   parts. Instead of creating new geometry for the dragger, it is
47   possible to specify an existing path in your scene to be used for
48   interaction. All picks on this path will be handled by the dragger.
49 
50   The SoInteractionKit is primarily an internal class used as a
51   superclass for the dragger classes, and it is unlikely that it
52   should be of interest to application programmers, unless you have
53   very special needs in your application.
54 
55   \NODEKIT_PRE_DIAGRAM
56 
57   \verbatim
58   CLASS SoInteractionKit
59   -->"this"
60         "callbackList"
61   -->   "topSeparator"
62   -->      "geomSeparator"
63   \endverbatim
64 
65   \NODEKIT_POST_DIAGRAM
66 
67 
68   \NODEKIT_PRE_TABLE
69 
70   \verbatim
71   CLASS SoInteractionKit
72   PVT   "this",  SoInteractionKit  ---
73         "callbackList",  SoNodeKitListPart [ SoCallback, SoEventCallback ]
74   PVT   "topSeparator",  SoSeparator  ---
75   PVT   "geomSeparator",  SoSeparator  ---
76   \endverbatim
77 
78   \NODEKIT_POST_TABLE
79 */
80 
81 #include <Inventor/nodekits/SoInteractionKit.h>
82 
83 #include <cstdlib>
84 
85 #include <Inventor/C/tidbits.h>
86 #include <Inventor/SoDB.h>
87 #include <Inventor/SoInput.h>
88 #include <Inventor/actions/SoSearchAction.h>
89 #include <Inventor/errors/SoDebugError.h>
90 #include <Inventor/errors/SoReadError.h>
91 #include <Inventor/lists/SbList.h>
92 #include <Inventor/lists/SoPathList.h>
93 #include <Inventor/misc/SoChildList.h>
94 #include <Inventor/nodes/SoSeparator.h>
95 #include <Inventor/nodes/SoSwitch.h>
96 #include <Inventor/nodes/SoText2.h>
97 #include <Inventor/sensors/SoFieldSensor.h>
98 
99 #include "tidbitsp.h"
100 #include "coindefs.h" // COIN_OBSOLETED()
101 #include "nodekits/SoSubKitP.h"
102 
103 /*!
104   \enum SoInteractionKit::CacheEnabled
105 
106   Enumeration of valid values for the cache control fields
107   SoInteractionKit::renderCaching,
108   SoInteractionKit::boundingBoxCaching,
109   SoInteractionKit::renderCulling and SoInteractionKit::pickCulling.
110 
111   The same values with the same semantics are present in this enum as
112   for SoSeparator::CacheEnabled, so see that documentation.
113 */
114 
115 /*!
116   \var SoSFEnum SoInteractionKit::renderCaching
117 
118   Controls the value of the SoSeparator::renderCaching field in the
119   SoInteractionKit catalog's topSeparator instance.
120 
121   See documentation of SoSeparator::renderCaching.
122 */
123 /*!
124   \var SoSFEnum SoInteractionKit::boundingBoxCaching
125 
126   Controls the value of the SoSeparator::boundingBoxCaching field in
127   the SoInteractionKit catalog's topSeparator instance.
128 
129   See documentation of SoSeparator::boundingBoxCaching.
130 */
131 /*!
132   \var SoSFEnum SoInteractionKit::renderCulling
133 
134   Controls the value of the SoSeparator::renderCulling field in the
135   SoInteractionKit catalog's topSeparator instance.
136 
137   See documentation of SoSeparator::renderCulling.
138 */
139 /*!
140   \var SoSFEnum SoInteractionKit::pickCulling
141 
142   Controls the value of the SoSeparator::pickCulling field in the
143   SoInteractionKit catalog's topSeparator instance.
144 
145   See documentation of SoSeparator::pickCulling.
146 */
147 
148 /*!
149   \var SoFieldSensor * SoInteractionKit::fieldSensor
150   Obsoleted in Coin.
151 */
152 /*!
153   \var SoFieldSensor * SoInteractionKit::oldTopSep
154   Obsoleted in Coin.
155 */
156 
157 #ifndef DOXYGEN_SKIP_THIS
158 
159 class SoInteractionKitP {
160 public:
SoInteractionKitP(SoInteractionKit * kit)161   SoInteractionKitP(SoInteractionKit * kit) : kit(kit) { }
162 
163   SoInteractionKit * kit;
164   SoFieldSensor * fieldsensor;
165   SoSeparator * connectedseparator;
166 
167   void connectFields(const SbBool onoff);
168   void attachSensor(const SbBool onoff);
169 
170   static void sensorCB(void *, SoSensor *);
171 
172   SoPathList surrogatepathlist;
173   SbList <SbName> surrogatenamelist;
174 
175   void addSurrogatePath(SoPath * path, const SbName & name);
176   void removeSurrogatePath(const SbName & partname);
177   void removeSurrogatePath(const int idx);
178   int findSurrogateIndex(const SbName & partname) const;
179   int findSurrogateInPath(const SoPath * path);
180 };
181 
182 #endif // DOXYGEN_SKIP_THIS
183 
184 
185 static SbList <SoNode*> * defaultdraggerparts = NULL;
186 
187 
188 //
189 // atexit callback used to unref() each draggerdefaults file
190 //
191 static void
defaultdraggerparts_cleanup(void)192 defaultdraggerparts_cleanup(void)
193 {
194   int n = defaultdraggerparts->getLength();
195   for (int i = 0; i < n; i++) {
196     (*defaultdraggerparts)[i]->unref();
197   }
198 }
199 
200 //
201 // atexit callback used to delete static data for this class
202 //
203 static void
interactionkit_cleanup(void)204 interactionkit_cleanup(void)
205 {
206   delete defaultdraggerparts;
207   defaultdraggerparts = NULL;
208 }
209 
210 #define PRIVATE(obj) ((obj)->pimpl)
211 
212 SO_KIT_SOURCE(SoInteractionKit);
213 
214 
215 /*!
216   Constructor.
217 */
SoInteractionKit(void)218 SoInteractionKit::SoInteractionKit(void)
219 {
220   PRIVATE(this) = new SoInteractionKitP(this);
221   SO_KIT_INTERNAL_CONSTRUCTOR(SoInteractionKit);
222 
223   SO_KIT_ADD_FIELD(renderCaching, (SoInteractionKit::AUTO));
224   SO_KIT_ADD_FIELD(boundingBoxCaching, (SoInteractionKit::AUTO));
225   SO_KIT_ADD_FIELD(renderCulling, (SoInteractionKit::AUTO));
226   SO_KIT_ADD_FIELD(pickCulling, (SoInteractionKit::AUTO));
227 
228   SO_KIT_DEFINE_ENUM_VALUE(CacheEnabled, ON);
229   SO_KIT_DEFINE_ENUM_VALUE(CacheEnabled, OFF);
230   SO_KIT_DEFINE_ENUM_VALUE(CacheEnabled, AUTO);
231 
232   SO_KIT_SET_SF_ENUM_TYPE(renderCaching, CacheEnabled);
233   SO_KIT_SET_SF_ENUM_TYPE(boundingBoxCaching, CacheEnabled);
234   SO_KIT_SET_SF_ENUM_TYPE(renderCulling, CacheEnabled);
235   SO_KIT_SET_SF_ENUM_TYPE(pickCulling, CacheEnabled);
236 
237 
238   // Note: we must use "" instead of , , to humour MS VisualC++ 6.
239 
240   SO_KIT_ADD_CATALOG_ENTRY(topSeparator, SoSeparator, TRUE, this, "", FALSE);
241   SO_KIT_ADD_CATALOG_ENTRY(geomSeparator, SoSeparator, TRUE, topSeparator, "", FALSE);
242 
243   SO_KIT_INIT_INSTANCE();
244 
245   PRIVATE(this)->connectedseparator = NULL;
246   PRIVATE(this)->fieldsensor = new SoFieldSensor(SoInteractionKit::fieldSensorCB, PRIVATE(this));
247   PRIVATE(this)->fieldsensor->setPriority(0);
248 
249   this->setUpConnections(TRUE, TRUE);
250 }
251 
252 /*!
253   Destructor.
254 */
~SoInteractionKit()255 SoInteractionKit::~SoInteractionKit()
256 {
257   PRIVATE(this)->connectFields(FALSE);
258   delete PRIVATE(this)->fieldsensor;
259   delete PRIVATE(this);
260 }
261 
262 // doc in super
263 void
initClass(void)264 SoInteractionKit::initClass(void)
265 {
266   defaultdraggerparts = new SbList <SoNode*>;
267   coin_atexit((coin_atexit_f *)defaultdraggerparts_cleanup, CC_ATEXIT_DRAGGERDEFAULTS);
268   coin_atexit((coin_atexit_f *)interactionkit_cleanup, CC_ATEXIT_NORMAL);
269 
270   SO_KIT_INTERNAL_INIT_CLASS(SoInteractionKit, SO_FROM_INVENTOR_1);
271 }
272 
273 /*!
274   Sets a part in the kit as a surrogate path. The \a partname part is
275   set to \c NULL, and the surrogate path is remembered. Following
276   picks on the surrogate path will be regarded as a pick on \a
277   partname.
278 */
279 SbBool
setPartAsPath(const SbName & partname,SoPath * path)280 SoInteractionKit::setPartAsPath(const SbName & partname,
281                                 SoPath * path)
282 {
283   return this->setAnySurrogatePath(partname, path, TRUE, TRUE);
284 }
285 
286 /*!
287   Sets the value of \a partname to \a node, and sets the part's field
288   to default (i.e. node will not be written on scene graph export).
289 
290   If \a onlyifdefault is \c TRUE, \a partname is only set if it is
291   already in the default state.
292 
293   The reason for this method is to make it possible for dragger
294   subclasses to avoid having their default parts written out on
295   export.
296 */
297 SbBool
setPartAsDefault(const SbName & partname,SoNode * node,SbBool onlyifdefault)298 SoInteractionKit::setPartAsDefault(const SbName & partname,
299                                    SoNode * node,
300                                    SbBool onlyifdefault)
301 {
302   return this->setAnyPartAsDefault(partname, node, FALSE, onlyifdefault);
303 }
304 
305 /*!
306   Find node in the global dictionary, and set as default.
307 
308   \sa setPartAsDefault()
309 */
310 SbBool
setPartAsDefault(const SbName & partname,const SbName & nodename,SbBool onlyifdefault)311 SoInteractionKit::setPartAsDefault(const SbName & partname,
312                                    const SbName & nodename,
313                                    SbBool onlyifdefault)
314 {
315   return this->setAnyPartAsDefault(partname, nodename, FALSE, onlyifdefault);
316 }
317 
318 
319 /*!
320   Checks if \a path is contained within any of the surrogate paths
321   in any interaction kits from this node down. Returns information
322   about the owner and the surrogate path if found, and \a fillargs is
323   \e TRUE. The returned path (\a pathToOwner) is not ref'ed, It's the
324   callers responsibility to ref and unref this path.
325 */
326 SbBool
isPathSurrogateInMySubgraph(const SoPath * path,SoPath * & pathToOwner,SbName & surrogatename,SoPath * & surrogatepath,SbBool fillargs)327 SoInteractionKit::isPathSurrogateInMySubgraph(const SoPath * path,
328                                               SoPath *& pathToOwner,
329                                               SbName  & surrogatename,
330                                               SoPath *& surrogatepath,
331                                               SbBool fillargs)
332 {
333   int idx = PRIVATE(this)->findSurrogateInPath(path);
334   if (idx >= 0) {
335     if (fillargs) {
336       pathToOwner = new SoPath(this); // a very short path
337       surrogatename = PRIVATE(this)->surrogatenamelist[idx];
338       surrogatepath = PRIVATE(this)->surrogatepathlist[idx];
339     }
340     return TRUE;
341   }
342   else {
343     SoSearchAction sa;
344     sa.setType(SoInteractionKit::getClassTypeId());
345     sa.setFind(SoSearchAction::ALL);
346     sa.setSearchingAll(TRUE);
347     sa.apply(this);
348     SoPathList & pathlist = sa.getPaths();
349     for (int i = 0; i < pathlist.getLength(); i++) {
350       SoInteractionKit * kit = (SoInteractionKit *)pathlist[i]->getTail();
351       assert(kit->isOfType(SoInteractionKit::getClassTypeId()));
352       int idx = kit->pimpl->findSurrogateInPath(path);
353       if (idx >= 0) {
354         if (fillargs) {
355           pathToOwner = pathlist[i]->copy();
356           surrogatename = kit->pimpl->surrogatenamelist[idx];
357           surrogatepath = kit->pimpl->surrogatepathlist[idx];
358         }
359         return TRUE;
360       }
361     }
362   }
363   return FALSE;
364 }
365 
366 /*!
367   \overload
368 */
369 SbBool
isPathSurrogateInMySubgraph(const SoPath * path)370 SoInteractionKit::isPathSurrogateInMySubgraph(const SoPath * path)
371 {
372   SoPath * dummypath, * dummypath2;
373   SbName dummyname;
374 
375   return this->isPathSurrogateInMySubgraph(path, dummypath,
376                                            dummyname, dummypath2, FALSE);
377 }
378 
379 /*!
380   Convenience method that sets the switch value for a switch node.
381   Checks if node != 0, and only sets the switch value if value
382   has changed.
383 */
384 void
setSwitchValue(SoNode * node,const int newVal)385 SoInteractionKit::setSwitchValue(SoNode * node, const int newVal)
386 {
387   if (node == NULL) return;
388   assert(node->isOfType(SoSwitch::getClassTypeId()));
389   SoSwitch * mySwitch = (SoSwitch *)node;
390   if (mySwitch->whichChild.getValue() != newVal) {
391     mySwitch->whichChild = newVal;
392   }
393 }
394 
395 // Doc in superclass. Overridden to copy the surrogate lists.
396 void
copyContents(const SoFieldContainer * fromFC,SbBool copyConnections)397 SoInteractionKit::copyContents(const SoFieldContainer * fromFC,
398                                SbBool copyConnections)
399 {
400   int i;
401   inherited::copyContents(fromFC, copyConnections);
402 
403   assert(fromFC->isOfType(SoInteractionKit::getClassTypeId()));
404   SoInteractionKit * kit = (SoInteractionKit *) fromFC;
405 
406   PRIVATE(this)->surrogatenamelist.truncate(0);
407   PRIVATE(this)->surrogatepathlist.truncate(0);
408 
409   const int n = kit->pimpl->surrogatenamelist.getLength();
410   for (i = 0; i < n; i++) {
411     PRIVATE(this)->surrogatenamelist.append(kit->pimpl->surrogatenamelist[i]);
412     PRIVATE(this)->surrogatepathlist.append(kit->pimpl->surrogatepathlist[i]);
413   }
414 }
415 
416 // Doc in superclass. Overridden to check topSeperator and fields
417 // after reading.
418 SbBool
readInstance(SoInput * in,unsigned short flags)419 SoInteractionKit::readInstance(SoInput * in, unsigned short flags)
420 {
421   SbBool ret = inherited::readInstance(in, flags); // will handle fields
422   if (ret) {
423     // remove surrogate paths where part != NULL and not an empty
424     // group or separator
425     int n = PRIVATE(this)->surrogatenamelist.getLength();
426     for (int i = 0; i < n; i++) {
427       SbName name = PRIVATE(this)->surrogatenamelist[i];
428       SoNode * node = this->getAnyPart(name, FALSE, FALSE, FALSE);
429       if (node && ((node->getTypeId() != SoGroup::getClassTypeId() &&
430                     node->getTypeId() != SoSeparator::getClassTypeId()) ||
431                    node->getChildren()->getLength())) {
432         n--; // don't forget this!
433         PRIVATE(this)->surrogatenamelist.remove(i);
434         PRIVATE(this)->surrogatepathlist.remove(i);
435       }
436     }
437   }
438   return ret;
439 }
440 
441 /*!
442   Reads default parts for a dragger.
443 
444   This method is called from dragger constructors to set up a
445   dragger's nodekit catalog of interaction and feedback geometry.
446 
447   \a fileName is the user-changeable resource file in the Inventor
448   file format, while \a defaultBuffer and \a defBufSize can point to
449   the statically compiled default parts.
450 
451   The environment variable \c SO_DRAGGER_DIR must be set to a valid
452   directory prefix for \a fileName, or no resource file will be loaded
453   (and \a defaultBuffer will be used instead).
454 
455   If both a \a fileName and a \a defaultBuffer is provided, the file
456   will be attempted found and loaded first, if that fails, the
457   geometry will be attempted read from the buffer.
458 */
459 void
readDefaultParts(const char * fileName,const char defaultBuffer[],int defBufSize)460 SoInteractionKit::readDefaultParts(const char * fileName,
461                                    const char defaultBuffer[],
462                                    int defBufSize)
463 {
464   // FIXME: it'd be great if this code could be changed so that the
465   // dragger parts file (if any) would just replace the parts from the
466   // default buffer. Then it would be possible to let the diskfile
467   // contain a subset of the full set of geometries for the dragger. I
468   // seem to remember that SGI Inventor works this way. 20020322 mortene.
469 
470   SoInput input;
471   SoNode * root = NULL;
472 
473   const char * draggerdir = coin_getenv("SO_DRAGGER_DIR");
474 
475   if (fileName && draggerdir) {
476     SbString fullname = draggerdir;
477     const char dirsep = '/';
478 
479     if (fullname.getLength() && fullname[fullname.getLength()-1] != dirsep) {
480       fullname += dirsep;
481     }
482     fullname += fileName;
483     if (input.openFile(fullname.getString(), TRUE)) {
484       root = (SoNode *)SoDB::readAll(&input);
485     }
486     else if (COIN_DEBUG) {
487       SoDebugError::post("SoInteractionKit::readDefaultParts",
488                          "Could not find file '%s' for the dragger "
489                          "default parts.",
490                          fullname.getString());
491     }
492   }
493 
494   if (!root && defaultBuffer) {
495     input.setBuffer((void *)defaultBuffer, defBufSize);
496     root = (SoNode *)SoDB::readAll(&input);
497   }
498 
499   if (root) {
500     root->ref(); // this node is unref'ed at exit
501 
502     // FIXME: the nodes are later picked up by SoNode::getByName(),
503     // which means this is a rather lousy and error-prone technique
504     // with the potential for namespace clashes. Should *at* *least*
505     // append a prefix "coininternal_draggerdefaultpart_" or something
506     // to all nodes. See also the related FIXME in
507     // setAnyPartAsDefault(SbName,SbName). 20020322 mortene.
508     defaultdraggerparts->append(root);
509   }
510   else {
511     SoDebugError::post("SoInteractionKit::readDefaultParts",
512                        "Dragger default parts not available.");
513   }
514 }
515 
516 /*!
517   Protected version of setPartAsDefault(), to make it possible to set
518   non-leaf and private parts (if \a anypart is \c TRUE).
519 
520   \sa setPartAsDefault()
521 */
522 SbBool
setAnyPartAsDefault(const SbName & partname,SoNode * node,SbBool COIN_UNUSED_ARG (anypart),SbBool onlyifdefault)523 SoInteractionKit::setAnyPartAsDefault(const SbName & partname,
524                                       SoNode * node,
525                                       SbBool COIN_UNUSED_ARG(anypart),
526                                       SbBool onlyifdefault)
527 {
528   SoBaseKit * kit = this;
529   int partNum;
530   SbBool isList;
531   int listIdx;
532   if (SoBaseKit::findPart(SbString(partname.getString()), kit, partNum,
533                           isList, listIdx, TRUE)) {
534     SoSFNode * field = kit->getCatalogInstances()[partNum];
535     // FIXME: default check not working properly. pederb, 2000-01-21
536     if (1 || (!onlyifdefault || field->isDefault())) {
537       kit->setPart(partNum, node);
538       field->setDefault(TRUE);
539     }
540     else {
541       if (COIN_DEBUG) {
542         SoDebugError::postInfo("SoInteractionKit::setAnyPartAsDefault",
543                                "no permission to set");
544       }
545     }
546   }
547   else if (COIN_DEBUG) {
548     SoDebugError::postInfo("SoInteractionKit::setAnyPartAsDefault",
549                            "part %s not found", partname.getString());
550   }
551 
552   // FIXME: this method is _always_ returning FALSE, which seems
553   // completely bogus. 20020322 mortene.
554   return FALSE;
555 }
556 
557 /*!
558   Protected version of setPartAsDefault(), to make it possible to set
559   non-leaf and private parts (if anypart is \c TRUE).
560 
561   \sa setPartAsDefault()
562 */
563 SbBool
setAnyPartAsDefault(const SbName & partname,const SbName & nodename,SbBool anypart,SbBool onlyifdefault)564 SoInteractionKit::setAnyPartAsDefault(const SbName & partname,
565                                       const SbName & nodename,
566                                       SbBool anypart,
567                                       SbBool onlyifdefault)
568 {
569   // FIXME: this is lame and error-prone -- default dragger-parts are
570   // actually just stored outside any scenegraph, and then picked up
571   // like this. We should at least prefix the node names with an
572   // internal namespace prefix. See also the related FIXME in
573   // readDefaultParts(). 20020322 mortene.
574   SoNode * node = (SoNode *)
575     SoBase::getNamedBase(nodename, SoNode::getClassTypeId());
576 
577   if (node) {
578     return this->setAnyPartAsDefault(partname, node, anypart, onlyifdefault);
579   }
580   else if (COIN_DEBUG && 1) { // debug
581     SoDebugError::postInfo("SoInteractionKit::setAnyPartAsDefault",
582                            "nodename %s not found", nodename.getString());
583 
584     // FIXME: temporary code, pederb 2000-01-21
585     node = new SoText2();
586     ((SoText2 *)node)->string = "Default dragger part not found";
587     return this->setAnyPartAsDefault(partname, node, anypart, onlyifdefault);
588   }
589   return FALSE;
590 }
591 
592 // FIXME: the API doc on setAnySurrogatePath() below stinks. Surrogate
593 // parts is such a useful mechanism that it deserves proper
594 // documentation. We should explain what it's good for, the details of
595 // setting up a surrogate part, and add in a small usage example (ie
596 // source code). 20021008 mortene.
597 
598 /*!
599   Protected version of setPartAsPath(), to make it possible to set
600   non-leaf and private parts.
601 
602   ("The nice thing about C++ is that only your friends can handle your
603   private parts.")
604 
605   \sa setPartAsPath()
606 */
607 SbBool
setAnySurrogatePath(const SbName & partname,SoPath * path,SbBool leafcheck,SbBool publiccheck)608 SoInteractionKit::setAnySurrogatePath(const SbName & partname,
609                                       SoPath * path,
610                                       SbBool leafcheck,
611                                       SbBool publiccheck)
612 {
613   SoBaseKit * kit = this;
614   int partNum;
615   SbBool isList;
616   int listIdx;
617   if (SoBaseKit::findPart(SbString(partname.getString()), kit, partNum,
618                           isList, listIdx, TRUE)) {
619     assert(kit->isOfType(SoInteractionKit::getClassTypeId()));
620     const SoNodekitCatalog * catalog = kit->getNodekitCatalog();
621     if (leafcheck && !catalog->isLeaf(partNum)) {
622       if (COIN_DEBUG) {
623         SoDebugError::postInfo("SoInteractionKit::setAnySurrogatePath",
624                                "part %s is not leaf", partname.getString());
625       }
626       return FALSE;
627     }
628     if (publiccheck && !catalog->isPublic(partNum)) {
629       if (COIN_DEBUG) {
630         SoDebugError::postInfo("SoInteractionKit::setAnySurrogatePath",
631                                "part %s is not public", partname.getString());
632       }
633       return FALSE;
634     }
635     int parentIdx = catalog->getParentPartNumber(partNum);
636     SoNode * parent = kit->getCatalogInstances()[parentIdx]->getValue();
637     if (parent->isOfType(SoSwitch::getClassTypeId())) {
638       SoNode * node = kit->getCatalogInstances()[partNum]->getValue();
639       SoType type = node->getTypeId();
640       if (type == SoGroup::getClassTypeId() ||
641           type == SoSeparator::getClassTypeId()) {
642         // replace with empty group to keep switch numbering
643         kit->setPart(partNum, (SoNode *)type.createInstance());
644       }
645       else { // set to NULL and update switch numbering
646         SoSwitch * sw = (SoSwitch *)parent;
647         int whichChild = sw->whichChild.getValue();
648         int partIdx = sw->findChild(node);
649         if (partIdx == whichChild) {
650           sw->whichChild.setValue(SO_SWITCH_NONE);
651         }
652         else if (partIdx < whichChild) {
653           sw->whichChild.setValue(whichChild-1);
654         }
655         kit->setPart(partNum, NULL);
656       }
657     }
658     else {
659       // set part to NULL
660       kit->setPart(partNum, NULL);
661     }
662     // add the path
663     ((SoInteractionKit *)kit)->pimpl->addSurrogatePath(path, catalog->getName(partNum));
664     return TRUE;
665   }
666   else if (COIN_DEBUG) {
667     SoDebugError::postInfo("SoInteractionKit::setAnyPartAsDefault",
668                            "part %s not found", partname.getString());
669   }
670   return FALSE;
671 }
672 
673 // doc in super
674 SbBool
setUpConnections(SbBool onoff,SbBool doitalways)675 SoInteractionKit::setUpConnections(SbBool onoff, SbBool doitalways)
676 {
677   if (onoff == this->connectionsSetUp && !doitalways)
678     return onoff;
679 
680   if (onoff) {
681     inherited::setUpConnections(onoff, FALSE);
682     PRIVATE(this)->connectFields(TRUE);
683     PRIVATE(this)->attachSensor(TRUE);
684   }
685   else {
686     PRIVATE(this)->attachSensor(FALSE);
687     PRIVATE(this)->connectFields(FALSE);
688     inherited::setUpConnections(onoff, FALSE);
689   }
690   return !(this->connectionsSetUp = onoff);
691 }
692 
693 // Doc in superclass.
694 SbBool
setPart(const int partNum,SoNode * node)695 SoInteractionKit::setPart(const int partNum, SoNode * node)
696 {
697   // Overriden to detect when part changes value. If a substitute path
698   // for that part exists, it must be cleared.
699 
700   PRIVATE(this)->removeSurrogatePath(this->getNodekitCatalog()->getName(partNum));
701   return inherited::setPart(partNum, node);
702 }
703 
704 // test if ok to set default and then do it if test succeeds
705 static void
test_set_default(SoSFEnum * field,int value)706 test_set_default(SoSFEnum * field, int value)
707 {
708   if (!(field->isConnected() && field->isConnectionEnabled()) &&
709       field->getValue() == value) field->setDefault(TRUE);
710 }
711 
712 // doc in super
713 void
setDefaultOnNonWritingFields(void)714 SoInteractionKit::setDefaultOnNonWritingFields(void)
715 {
716   this->topSeparator.setDefault(TRUE);
717   this->geomSeparator.setDefault(TRUE);
718 
719   test_set_default(&this->renderCaching, SoInteractionKit::AUTO);
720   test_set_default(&this->boundingBoxCaching, SoInteractionKit::AUTO);
721   test_set_default(&this->pickCulling, SoInteractionKit::AUTO);
722   test_set_default(&this->renderCulling, SoInteractionKit::AUTO);
723 
724   const SoNodekitCatalog * catalog = this->getNodekitCatalog();
725 
726   for (int i = 1; i < this->getCatalogInstances().getLength(); i++) {
727     if (!catalog->isLeaf(i)) {
728       SoNode * node = this->getCatalogInstances()[i]->getValue();
729       if (node && node->getTypeId() == SoSwitch::getClassTypeId()) {
730         this->getCatalogInstances()[i]->setDefault(TRUE);
731       }
732     }
733   }
734 
735   inherited::setDefaultOnNonWritingFields();
736 }
737 
738 // doc in super
739 SbBool
setPart(const SbName & partname,SoNode * from)740 SoInteractionKit::setPart(const SbName & partname, SoNode * from)
741 {
742   // Overridden only to fool some incredibly stupid behaviour in the
743   // gcc 2.95.2 compiler, who couldn't figure out I wanted to call
744   // this function in SoBaseKit, but instead insisted that I tried to
745   // call SoInteractionKit::setPart(int, SoNode *). Cheeessssh.
746   // <pederb>.
747   return inherited::setPart(partname, from);
748 }
749 
750 /*! \COININTERNAL */
751 void
fieldSensorCB(void * d,SoSensor * s)752 SoInteractionKit::fieldSensorCB(void * d, SoSensor * s)
753 {
754   SoInteractionKitP::sensorCB(d, s);
755 }
756 
757 /*!
758   Obsoleted in Coin.
759 */
760 void
connectSeparatorFields(SoSeparator * COIN_UNUSED_ARG (dest),SbBool onOff)761 SoInteractionKit::connectSeparatorFields(SoSeparator * COIN_UNUSED_ARG(dest), SbBool onOff)
762 {
763   COIN_OBSOLETED();
764   SoDebugError::postWarning("SoInteractionKit::connectSeparatorFields",
765                             "SoSeparator* input argument ignored, "
766                             "using topSeparator");
767   PRIVATE(this)->connectFields(onOff);
768 }
769 
770 #undef PRIVATE
771 
772 /*** methods for SoInteractionKitP are below *****************************/
773 
774 #ifndef DOXYGEN_SKIP_THIS
775 
776 //
777 // checks if partname is in surrogate list. Returns index, -1 if not found.
778 //
779 int
findSurrogateIndex(const SbName & partname) const780 SoInteractionKitP::findSurrogateIndex(const SbName & partname) const
781 {
782   int i, n = this->surrogatenamelist.getLength();
783   for (i = 0; i < n; i++) {
784     if (this->surrogatenamelist[i] == partname) return i;
785   }
786   return -1;
787 }
788 
789 //
790 // removes surrogate path with name 'partname'
791 //
792 void
removeSurrogatePath(const SbName & partname)793 SoInteractionKitP::removeSurrogatePath(const SbName & partname)
794 {
795   int idx = this->findSurrogateIndex(partname);
796   if (idx >= 0) this->removeSurrogatePath(idx);
797 }
798 
799 //
800 // removes a specified surrogate path
801 //
802 void
removeSurrogatePath(const int idx)803 SoInteractionKitP::removeSurrogatePath(const int idx)
804 {
805   assert(idx >= 0 && idx < this->surrogatenamelist.getLength());
806   this->surrogatenamelist.remove(idx);
807   this->surrogatepathlist.remove(idx);
808 }
809 
810 //
811 // return index of surrogate path that is contained within path,
812 // or -1 if none found.
813 //
814 int
findSurrogateInPath(const SoPath * path)815 SoInteractionKitP::findSurrogateInPath(const SoPath * path)
816 {
817   int n = this->surrogatepathlist.getLength();
818   for (int i = 0; i < n; i++) {
819     if (path->containsPath(this->surrogatepathlist[i])) return i;
820   }
821   return -1;
822 }
823 
824 //
825 // adds or replaces a surrogate path
826 //
827 void
addSurrogatePath(SoPath * path,const SbName & name)828 SoInteractionKitP::addSurrogatePath(SoPath * path, const SbName & name)
829 {
830   int idx = this->findSurrogateIndex(name);
831   if (idx >= 0) {
832     this->surrogatepathlist.remove(idx);
833     this->surrogatenamelist.remove(idx);
834   }
835   this->surrogatepathlist.append(path);
836   this->surrogatenamelist.append(name);
837 }
838 
839 
840 //
841 // connect fields in topSeparator to the fields in this node.
842 //
843 void
connectFields(const SbBool onoff)844 SoInteractionKitP::connectFields(const SbBool onoff)
845 {
846   if (this->connectedseparator) { // always disconnect
847     this->connectedseparator->renderCaching.disconnect();
848     this->connectedseparator->boundingBoxCaching.disconnect();
849     this->connectedseparator->renderCulling.disconnect();
850     this->connectedseparator->pickCulling.disconnect();
851     this->connectedseparator->unref();
852     this->connectedseparator = NULL;
853   }
854   if (onoff) {
855     SoSeparator * sep = (SoSeparator*) this->kit->topSeparator.getValue();
856     if (sep) {
857       this->connectedseparator = sep;
858       this->connectedseparator->ref(); // ref to make sure pointer is legal
859       sep->renderCaching.connectFrom(&this->kit->renderCaching);
860       sep->boundingBoxCaching.connectFrom(&this->kit->boundingBoxCaching);
861       sep->renderCulling.connectFrom(&this->kit->renderCulling);
862       sep->pickCulling.connectFrom(&this->kit->pickCulling);
863     }
864   }
865 }
866 
867 //
868 // attach sensor to topSeparator if onoff, detach otherwise
869 //
870 void
attachSensor(const SbBool onoff)871 SoInteractionKitP::attachSensor(const SbBool onoff)
872 {
873   if (onoff) {
874     if (this->fieldsensor->getAttachedField() != &this->kit->topSeparator) {
875       this->fieldsensor->attach(&this->kit->topSeparator);
876     }
877   }
878   else {
879     if (this->fieldsensor->getAttachedField()) this->fieldsensor->detach();
880   }
881 }
882 
883 //
884 // callback from field sensor connected to topSeparator
885 //
886 void
sensorCB(void * data,SoSensor *)887 SoInteractionKitP::sensorCB(void * data, SoSensor *)
888 {
889   SoInteractionKitP * thisp = (SoInteractionKitP*) data;
890   if (thisp->connectedseparator != thisp->kit->topSeparator.getValue()) {
891     thisp->connectFields(TRUE);
892   }
893 }
894 
895 #endif // DOXYGEN_SKIP_THIS
896 #endif // HAVE_NODEKITS
897