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