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 SoField SoField.h Inventor/fields/SoField.h
35 \brief The SoField class is the top-level abstract base class for fields.
36
37 \ingroup fields
38
39 Fields is the mechanism used throughout Coin for encapsulating basic
40 data types to detect changes made to them, and to provide
41 conversion, import and export facilities.
42
43 Almost all public properties in nodes are stored in fields, and so
44 are the inputs and outputs of engines. So fields can be viewed as
45 the major mechanism for scenegraph nodes and engines to expose their
46 public API.
47
48 Forcing data modification to go through a public function interface
49 while hiding the data members makes it possible to automatically
50 detect and react upon changes in the data structures set up by the
51 application programmer.
52
53 E.g. the default behavior when changing the value of a field in a
54 scenegraph node is that there'll automatically be a chain of
55 notifications -- from the field to the owner node, from that node to
56 it's parent node, etc all the way through to the top-most root node,
57 where the need for a rendering update will be signalled to the
58 application.
59
60 (This notification mechanism is the underlying feature that makes the
61 Coin library classify as a so-called \e data-driven scenegraph API.
62
63 The practical consequences of this is that rendering and many other
64 processing actions is default scheduled to \e only happen when
65 something has changed in the retained data structures, making the
66 Coin library under normal circumstances \e much less CPU intensive
67 than so-called "application-driven" scenegraph API, like for
68 instance SGI IRIS Performer, which are continuously re-rendering
69 even when nothing has changed in the data structures or with the
70 camera viewport.)
71
72 Storing data members as fields also provides other conveniences for
73 the application programmer:
74
75 \li Fields can be connected to other fields. This makes it for
76 instance possible to have "self-updating" scenes, ie you can set
77 up scenes where entities \e automatically react to changes in
78 other entities. This also provides a necessary mechanism for
79 having "auto-animating" scenes, as it is possible to connect any
80 field to the global field named \c realTime, providing a
81 wall-clock timer.
82
83 \li When connecting fields to each other, Coin has built-in
84 mechanisms for automatically converting between different field
85 types.
86
87 \li Fields provide persistance for scenegraph import (and export)
88 operations. This includes animating entities, so animations can
89 be stored within ordinary Inventor format files.
90
91 \li Fields provides features for introspection: they have a
92 type-system, just like for nodes and actions, they are named,
93 and it is also possible to find out which node, engine or other
94 entity owns a field.
95
96 \li Fields can hold multiple values. Multi-value fields comes with a
97 much higher level interface abstraction than standard C/C++
98 arrays.
99
100 Note: there are some field classes which has been obsoleted from the
101 Open Inventor API. They are: SoSFLong, SoSFULong, SoMFLong and
102 SoMFULong. You should use these classes instead (respectively):
103 SoSFInt32, SoSFUInt32, SoMFInt32 and SoMFUInt32.
104
105 \TOOLMAKER_REF
106
107 \sa SoFieldContainer, SoFieldData
108 */
109
110 #include <Inventor/fields/SoField.h>
111
112 #include <cassert>
113 #include <cstring>
114
115 #include <Inventor/fields/SoFields.h>
116
117 #include <Inventor/SoDB.h>
118 #include <Inventor/SoInput.h>
119 #include <Inventor/SoOutput.h>
120 #include <Inventor/actions/SoWriteAction.h>
121 #include <Inventor/engines/SoNodeEngine.h>
122 #include <Inventor/errors/SoDebugError.h>
123 #include <Inventor/errors/SoReadError.h>
124 #include <Inventor/lists/SoEngineList.h>
125 #include <Inventor/lists/SoEngineOutputList.h>
126 #include <Inventor/misc/SoProtoInstance.h>
127 #include <Inventor/nodes/SoNode.h>
128 #include <Inventor/sensors/SoDataSensor.h>
129
130 #ifdef HAVE_CONFIG_H
131 #include "config.h"
132 #endif // HAVE_CONFIG_H
133 #include "SbBasicP.h"
134 #include "engines/SoConvertAll.h"
135 #include "fields/SoGlobalField.h"
136 #include "io/SoWriterefCounter.h"
137 #include "misc/SoConfigSettings.h"
138 #include "threads/threadsutilp.h"
139 #include "tidbitsp.h"
140 inline unsigned int SbHashFunc(const void * key);
141 #include "misc/SbHash.h"
SbHashFunc(const void * key)142 inline unsigned int SbHashFunc(const void * key)
143 {
144 return SbHashFunc(reinterpret_cast<size_t>(key));
145 }
146 #include "coindefs.h" // COIN_STUB()
147
148 #ifdef COIN_THREADSAFE
149 #include "threads/recmutexp.h"
150 #define SOFIELD_RECLOCK (void) cc_recmutex_internal_field_lock()
151 #define SOFIELD_RECUNLOCK (void) cc_recmutex_internal_field_unlock()
152
153 #else // COIN_THREADSAFE
154
155 #define SOFIELD_RECLOCK
156 #define SOFIELD_RECUNLOCK
157
158 #endif // !COIN_THREADSAFE
159
160 static const int SOFIELD_GET_STACKBUFFER_SIZE = 1024;
161 // need one static mutex for field_buffer in SoField::get(SbString &)
162 static void * sofield_mutex = NULL;
163
164 // flags for this->statusbits
165
166 static const char IGNOREDCHAR = '~';
167 static const char CONNECTIONCHAR = '=';
168 /*
169 This class is used to aid in "multiplexing" the pointer member of
170 SoField. This is a way to achieve the goal of using minimum storage
171 space for SoField classes in the default case (which is important,
172 as fields are ubiquitous in Coin). The default case means no
173 connections and only a field container given. If any connections are
174 made (either "to" or "from"), we allocate an SoConnectStorage and
175 move the field container pointer into it, while swapping in the
176 SoConnectStorage pointer where the field container pointer used to
177 be.
178 */
179 class SoConnectStorage {
180 public:
SoConnectStorage(SoFieldContainer * c,SoType t)181 SoConnectStorage(SoFieldContainer * c, SoType t)
182 : container(c),
183 lastnotify(NULL),
184 fieldtype(t),
185 maptoconverter(13) // save about ~1kB vs default nr of buckets
186 {
187 }
188
189 #if COIN_DEBUG
190 // Check that everything has been emptied.
~SoConnectStorage()191 ~SoConnectStorage()
192 {
193 assert(this->maptoconverter.getNumElements() == 0);
194
195 assert(masterfields.getLength() == 0);
196 assert(masterengineouts.getLength() == 0);
197
198 assert(slaves.getLength() == 0);
199 assert(auditors.getLength() == 0);
200 }
201 #endif // COIN_DEBUG
202
203 // The container this field is part of.
204 SoFieldContainer * container;
205
206 // List of masters we're connected to as a slave. Use maptoconverter
207 // dict to find SoFieldConverter engine in the connection (if any).
208 SoFieldList masterfields;
209 SoEngineOutputList masterengineouts;
210 // Fields which are slaves to us. Use maptoconverter dict to find
211 // SoFieldConverter engine in the connection (if any).
212 SoFieldList slaves;
213 // Direct auditors of us.
214 SoAuditorList auditors;
215
216 // used to track the last notification (for fanIn handling)
217 void * lastnotify;
218
219 // Convenience functions for adding, removing and finding mappings.
220
addConverter(const void * item,SoFieldConverter * converter)221 void addConverter(const void * item, SoFieldConverter * converter)
222 {
223 // "item" can be SoField* or SoEngineOutput*.
224
225 // FIXME: this probably hashes horribly bad, as the item value is
226 // a pointer and is therefore address-aligned (lower 32 (?) bits
227 // are all 0). 20010911 mortene.
228 this->maptoconverter.put(item, converter);
229 }
230
removeConverter(const void * item)231 void removeConverter(const void * item)
232 {
233 size_t ok = this->maptoconverter.erase(item);
234 assert(ok);
235 }
236
findConverter(const void * item)237 SoFieldConverter * findConverter(const void * item)
238 {
239 SoFieldConverter * val;
240 if (!this->maptoconverter.get(item, val)) { return NULL; }
241 return val;
242 }
243
hasFanIn(void)244 SbBool hasFanIn(void) {
245 return (this->masterfields.getLength() + this->masterengineouts.getLength()) > 1;
246 }
findFanInEngine(void) const247 int findFanInEngine(void) const {
248 for (int i = 0; i < this->masterengineouts.getLength(); i++) {
249 SoEngineOutput * o = this->masterengineouts[i];
250 if (o->isNodeEngineOutput()) {
251 if (static_cast<void *>(o->getNodeContainer()) == this->lastnotify) return i;
252 }
253 else {
254 if (static_cast<void *>(o->getContainer()) == this->lastnotify) return i;
255 }
256 }
257 return -1;
258 }
findFanInField(void) const259 int findFanInField(void) const {
260 for (int i = 0; i < this->masterfields.getLength(); i++) {
261 if (static_cast<void *>(this->masterfields[i]->getContainer()) == this->lastnotify) return i;
262 }
263 return -1;
264 }
265
266
267 // Provides us with a hack to get at a master field's type in code
268 // called from its constructor (SoField::getTypeId() is virtual and
269 // can't be used).
270 //
271 // (Used in the master::~SoField() -> slave::disconnect(master)
272 // chain.)
273 SoType fieldtype;
274 void add_vrml2_routes(SoOutput * out, const SoField * f);
275
276 private:
277 // Dictionary of void* -> SoFieldConverter* mappings.
278 SbHash<const void *, SoFieldConverter *> maptoconverter;
279
280 };
281
282 // helper function. Used to check if field is in a vrml2 node
283 static SbBool
is_vrml2_field(const SoField * f)284 is_vrml2_field(const SoField * f)
285 {
286 assert(f);
287 SoFieldContainer * fc = f->getContainer();
288 // test fc to support fields with no container
289 if (fc && fc->isOfType(SoNode::getClassTypeId())) {
290 if (fc->isOfType(SoProtoInstance::getClassTypeId())) return TRUE;
291 if (coin_assert_cast<SoNode *>(fc)->getNodeType() & SoNode::VRML2) return TRUE;
292 }
293
294 return FALSE;
295 }
296
297 //
298 // add all connections to this field as routes in SoOutput. SoOutput
299 // will decide when to write the ROUTEs.
300 //
301 void
add_vrml2_routes(SoOutput * out,const SoField * f)302 SoConnectStorage::add_vrml2_routes(SoOutput * out, const SoField * f)
303 {
304 SoFieldContainer * tofc = f->getContainer();
305 assert(tofc);
306 SbName toname, fromname;
307 (void) tofc->getFieldName(f, toname);
308
309 int i;
310 for (i = 0; i < this->masterfields.getLength(); i++) {
311 SoField * master = this->masterfields[i];
312 SoFieldContainer * fc = master->getContainer();
313 assert(fc);
314 (void) fc->getFieldName(master, fromname);
315
316 if (out->getStage() == SoOutput::COUNT_REFS) {
317 fc->addWriteReference(out, TRUE);
318 tofc->addWriteReference(out, TRUE);
319 }
320 else {
321 out->addRoute(fc, fromname, tofc, toname);
322 }
323 }
324 for (i = 0; i < this->masterengineouts.getLength(); i++) {
325 SoEngineOutput * engineout = this->masterengineouts[i];
326 SoFieldContainer * fc = engineout->getFieldContainer();
327 if (engineout->isNodeEngineOutput()) {
328 SoNodeEngine * engine = engineout->getNodeContainer();
329 assert(engine);
330 (void) engine->getOutputName(engineout, fromname);
331 }
332 else {
333 SoEngine * engine = engineout->getContainer();
334 assert(engine);
335 (void) engine->getOutputName(engineout, fromname);
336 }
337 if (out->getStage() == SoOutput::COUNT_REFS) {
338 fc->addWriteReference(out, TRUE);
339 tofc->addWriteReference(out, TRUE);
340 }
341 else {
342 out->addRoute(fc, fromname, tofc, toname);
343 }
344 }
345 }
346
347 // Collects some private code for SoField.
348 //
349 // Note that there is no private implementation data pointer (aka
350 // "pimpl" or Cheshire Cat) for the SoField instances, as they should
351 // be as slim as possible. Therefore, this class only contains static
352 // functions.
353 class SoFieldP {
354 public:
355 // Convenience method to extract a string that identifies the field
356 // with as much relevant info as possible. Used from other debug
357 // output code.
getDebugIdString(const SoField * f)358 static SbString getDebugIdString(const SoField * f)
359 {
360 SoFieldContainer * fcontainer = f->getContainer();
361 SbName fname("<no-container>");
362 if (fcontainer) {
363 SbBool ok = fcontainer->getFieldName(f, fname);
364 if (!ok) { fname = "<not-yet-added>"; }
365 }
366 SbString s;
367 s.sprintf("field==%p/%s/'%s'",
368 f,
369 f->getTypeId().getName().getString(),
370 fname.getString());
371 return s;
372 }
373
374 static SbHash<char *, char **> * getReallocHash(void);
375 static void * hashRealloc(void * bufptr, size_t size);
376
377 static SbHash<char *, char **> * ptrhash;
378 };
379
380 SbHash<char *, char **> * SoFieldP::ptrhash = NULL;
381
382 extern "C" {
383 // atexit callbacks
384 static void SoField_cleanupClass(void);
385 static void hashExitCleanup(void);
386 static void field_mutex_cleanup(void);
387 }
388
389 SbHash<char *, char **> *
getReallocHash(void)390 SoFieldP::getReallocHash(void)
391 {
392 // FIXME: protect with mutex?
393 if (SoFieldP::ptrhash == NULL) {
394 SoFieldP::ptrhash = new SbHash<char *, char **>;
395 coin_atexit(hashExitCleanup, CC_ATEXIT_NORMAL);
396 }
397 return SoFieldP::ptrhash;
398 }
399
400 void
hashExitCleanup(void)401 hashExitCleanup(void)
402 {
403 assert(SoFieldP::ptrhash->getNumElements() == 0);
404 delete SoFieldP::ptrhash;
405 SoFieldP::ptrhash = NULL;
406 }
407
408 void *
hashRealloc(void * bufptr,size_t size)409 SoFieldP::hashRealloc(void * bufptr, size_t size)
410 {
411 CC_MUTEX_LOCK(sofield_mutex);
412
413 char ** bufptrptr = NULL;
414 SbBool ok = SoFieldP::ptrhash->get(static_cast<char *>(bufptr), bufptrptr);
415 assert(ok);
416
417 // If *bufptrptr contains a NULL pointer, this is the first
418 // invocation and the initial memory buffer was on the stack.
419 char * newbuf;
420 if (*bufptrptr == NULL) {
421 // if initial buffer was on the stack, we need to manually copy
422 // the data into the new buffer.
423 newbuf = static_cast<char *>(malloc(size));
424 memcpy(newbuf, bufptr, SOFIELD_GET_STACKBUFFER_SIZE);
425 }
426 else {
427 newbuf = static_cast<char *>(realloc(bufptr, size));
428 }
429 if (newbuf != bufptr) {
430 size_t isok = SoFieldP::ptrhash->erase(static_cast<char *>(bufptr));
431 assert(isok);
432 *bufptrptr = newbuf;
433 SoFieldP::ptrhash->put(newbuf, bufptrptr);
434 }
435
436 CC_MUTEX_UNLOCK(sofield_mutex);
437
438 return newbuf;
439 }
440
441 // *************************************************************************
442
443 // Documentation for abstract methods.
444
445 // FIXME: grab better version of getTypeId() doc from SoBase, SoAction
446 // and / or SoDetail. 20010913 mortene.
447 /*!
448 \fn SoType SoField::getTypeId(void) const
449
450 Returns the type identification instance which uniquely identifies
451 the Coin field class the object belongs to.
452
453 \sa getClassTypeId(), SoType
454 */
455
456 /*!
457 \fn SbBool SoField::isSame(const SoField & f) const
458 Check for equal type and value(s).
459 */
460
461 /*!
462 \fn void SoField::copyFrom(const SoField & f)
463
464 Copy value(s) from \a f into this field. \a f must be of the same
465 type as this field.
466 */
467
468 /*!
469 \fn SbBool SoField::readValue(SoInput * in)
470 Read field value(s).
471 */
472
473 /*!
474 \fn void SoField::writeValue(SoOutput * out) const
475 Write field value(s).
476 */
477
478
479 // *************************************************************************
480
481 SoType SoField::classTypeId STATIC_SOTYPE_INIT;
482
483 // *************************************************************************
484
485 // used to detect when a field that is already destructed is used
486 #define FLAG_ALIVE_PATTERN 0xbeef0000
487
488 // private methods. Inlined inside this file only.
489
490 // clear bits in statusbits
491 inline void
clearStatusBits(const unsigned int bits)492 SoField::clearStatusBits(const unsigned int bits)
493 {
494 this->statusbits &= ~bits;
495 }
496
497 // sets bits in statusbits
498 inline void
setStatusBits(const unsigned int bits)499 SoField::setStatusBits(const unsigned int bits)
500 {
501 this->statusbits |= bits;
502 }
503
504 // return TRUE if any of bits is set
505 inline SbBool
getStatus(const unsigned int bits) const506 SoField::getStatus(const unsigned int bits) const
507 {
508 return (this->statusbits & bits) != 0;
509 }
510
511 // convenience method for clearing or setting based on boolean value
512 // returns TRUE if any bitflag changed value
513 inline SbBool
changeStatusBits(const unsigned int bits,const SbBool onoff)514 SoField::changeStatusBits(const unsigned int bits, const SbBool onoff)
515 {
516 unsigned int oldval = this->statusbits;
517 unsigned int newval = oldval;
518 if (onoff) newval |= bits;
519 else newval &= ~bits;
520 if (oldval != newval) {
521 this->statusbits = newval;
522 return TRUE;
523 }
524 return FALSE;
525 }
526
527 // returns TRUE if this field has ext storage
528 inline SbBool
hasExtendedStorage(void) const529 SoField::hasExtendedStorage(void) const
530 {
531 return this->getStatus(FLAG_EXTSTORAGE);
532 }
533
534
535 /*!
536 This is the base constructor for field classes. It takes care of
537 doing the common parts of data initialization in fields.
538 */
SoField(void)539 SoField::SoField(void)
540 : container(NULL)
541 {
542 this->statusbits = 0;
543 this->setStatusBits(FLAG_DONOTIFY |
544 FLAG_ISDEFAULT |
545 FLAG_ENABLECONNECTS|
546 FLAG_ALIVE_PATTERN);
547 }
548
549 /*!
550 Destructor. Disconnects ourself from any connected field or engine
551 before we disconnect all auditors on the field.
552 */
~SoField()553 SoField::~SoField()
554 {
555 // set status bit to avoid evaluating this field while
556 // disconnecting connections.
557 this->setStatusBits(FLAG_ISDESTRUCTING);
558
559 #if COIN_DEBUG_EXTRA
560 int wLevel =
561 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
562 if (wLevel>=3)
563 SoDebugError::postInfo("SoField::~SoField", "destructing %p", this);
564 #endif //COIN_DEBUG_EXTRA
565
566 // Disconnect ourself from all connections where this field is the
567 // slave.
568 this->disconnect();
569
570 if (this->hasExtendedStorage()) {
571
572 // Disconnect slave fields using us as a master.
573 while (this->storage->slaves.getLength()) {
574 this->storage->slaves[0]->disconnect(this);
575 }
576
577 // Disconnect other auditors.
578 while (this->storage->auditors.getLength()) {
579 SoNotRec::Type type = this->storage->auditors.getType(0);
580 void * obj = this->storage->auditors.getObject(0);
581
582 switch (type) {
583 case SoNotRec::ENGINE:
584 static_cast<SoEngineOutput *>(obj)->removeConnection(this);
585 break;
586
587 case SoNotRec::CONTAINER:
588 assert(FALSE && "Container should not be in auditorlist");
589 break;
590
591 case SoNotRec::SENSOR:
592 static_cast<SoDataSensor *>(obj)->dyingReference();
593 break;
594
595 case SoNotRec::FIELD:
596 assert(FALSE); // should not happen, as slave fields are removed first.
597 break;
598
599 default:
600 assert(FALSE); // no other allowed types.
601 break;
602 }
603 }
604
605 delete this->storage;
606 }
607 #if COIN_DEBUG_EXTRA
608 if (wLevel>=3)
609 SoDebugError::postInfo("SoField::~SoField", "%p done", this);
610 #endif //COIN_DEBUG_EXTRA
611
612 this->clearStatusBits(FLAG_ALIVE_PATTERN);
613 }
614
615 // atexit
616 void
field_mutex_cleanup(void)617 field_mutex_cleanup(void)
618 {
619 CC_MUTEX_DESTRUCT(sofield_mutex);
620 }
621
622 /*!
623 Internal method called upon initialization of the library (from
624 SoDB::init()) to set up the type system.
625 */
626 void
initClass(void)627 SoField::initClass(void)
628 {
629 // Make sure we only initialize once.
630 assert(SoField::classTypeId == SoType::badType());
631
632 CC_MUTEX_CONSTRUCT(sofield_mutex);
633 coin_atexit(field_mutex_cleanup, CC_ATEXIT_NORMAL);
634
635 SoField::classTypeId = SoType::createType(SoType::badType(), "Field");
636 SoField::initClasses();
637 coin_atexit(SoField_cleanupClass, CC_ATEXIT_NORMAL);
638 }
639
640 void
SoField_cleanupClass(void)641 SoField_cleanupClass(void)
642 {
643 SoField::cleanupClass();
644 }
645
646 void
cleanupClass(void)647 SoField::cleanupClass(void)
648 {
649 SoField::classTypeId STATIC_SOTYPE_INIT;
650 }
651
652 /*!
653 Sets the flag which indicates whether or not the field should be
654 ignored during certain operations.
655
656 The effect of this flag depends on what type of field it is used on,
657 and the type of the node which includes the field.
658
659 This flag is represented in Inventor files by a ~ behind the field
660 name. The flag is in other words persistent.
661
662 \sa isIgnored()
663 */
664 void
setIgnored(SbBool ignore)665 SoField::setIgnored(SbBool ignore)
666 {
667 if (this->changeStatusBits(FLAG_IGNORE, ignore)) {
668 this->valueChanged(FALSE);
669 }
670 }
671
672 /*!
673 Returns the ignore flag.
674
675 \sa setIgnored()
676 */
677 SbBool
isIgnored(void) const678 SoField::isIgnored(void) const
679 {
680 return this->getStatus(FLAG_IGNORE);
681 }
682
683 /*!
684 Set whether or not this field should be marked as containing a
685 default value.
686
687 \sa isDefault()
688 */
689 void
setDefault(SbBool def)690 SoField::setDefault(SbBool def)
691 {
692 #if COIN_DEBUG_EXTRA
693 int wLevel =
694 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
695 if (wLevel>=3) {
696 SbString finfo = SoFieldP::getDebugIdString(this);
697 SoDebugError::postInfo("SoField::setDefault", "%s, setDefault(%s)",
698 finfo.getString(), def ? "TRUE" : "FALSE");
699 }
700 #endif //COIN_DEBUG_EXTRA
701
702 (void) this->changeStatusBits(FLAG_ISDEFAULT, def);
703 }
704
705 /*!
706 Check if the field contains its default value. Fields which has
707 their default value intact will normally not be included in the
708 output when writing scene graphs out to a file, for instance.
709
710 \sa setDefault()
711 */
712 SbBool
isDefault(void) const713 SoField::isDefault(void) const
714 {
715 return this->getStatus(FLAG_ISDEFAULT);
716 }
717
718 /*!
719 Returns a unique type identifier for this field class.
720
721 \sa getTypeId(), SoType
722 */
723 SoType
getClassTypeId(void)724 SoField::getClassTypeId(void)
725 {
726 return SoField::classTypeId;
727 }
728
729 /*!
730 Check if this instance is of a derived type or is the same type as
731 the one given with the \a type parameter.
732 */
733 SbBool
isOfType(const SoType type) const734 SoField::isOfType(const SoType type) const
735 {
736 return this->getTypeId().isDerivedFrom(type);
737 }
738
739 /*!
740 This sets a \a flag value which indicates whether or not the set up
741 connection should be considered active. For as long as the "enable
742 connection" flag is \c FALSE, no value propagation will be done from
743 any connected source field, engine or interpolator into this field.
744
745 If the connection is first disabled and then enabled again, the
746 field will automatically be synchronized with any master field,
747 engine or interpolator.
748
749 \sa isConnectionEnabled()
750 */
751 void
enableConnection(SbBool flag)752 SoField::enableConnection(SbBool flag)
753 {
754 SbBool oldval = this->getStatus(FLAG_ENABLECONNECTS);
755 (void) this->changeStatusBits(FLAG_ENABLECONNECTS, flag);
756 if (!oldval && flag) this->setDirty(TRUE);
757 }
758
759 /*!
760 Return the current status of the connection enabled flag.
761
762 \sa enableConnection()
763 */
764 SbBool
isConnectionEnabled(void) const765 SoField::isConnectionEnabled(void) const
766 {
767 return this->getStatus(FLAG_ENABLECONNECTS);
768 }
769
770 /*!
771 Connects this field as a slave to \a master. This means that the
772 value of this field will be automatically updated when \a master is
773 changed (as long as the connection also is enabled).
774
775 If this field had any connections to master fields beforehand, these
776 are all broken up if \a append is \c FALSE.
777
778 Call with \a notnotify if you want to avoid the initial notification
779 of connected auditors (a.k.a. \e slaves).
780
781 Function will return \c TRUE unless:
782
783 \li If the field connected \e from has a different type from the
784 field connected \e to, a field converter is inserted. For some
785 combinations of fields no such conversion is possible, and we'll
786 return \c FALSE.
787
788 \li If this field is already connected to the \a master, we will
789 return \c FALSE.
790
791 \sa enableConnection(), isConnectionEnabled(), isConnectedFromField()
792 \sa getConnectedField(), appendConnection(SoField *)
793 */
794 SbBool
connectFrom(SoField * master,SbBool notnotify,SbBool append)795 SoField::connectFrom(SoField * master, SbBool notnotify, SbBool append)
796 {
797 // detect and ref() global fields. This is done to automatically
798 // detect when the last reference to a global field is deleted
799 if (master->getContainer() && master->getContainer()->isOfType(SoGlobalField::getClassTypeId())) {
800 master->getContainer()->ref();
801 }
802 // Initialize. /////////////////////////////////////////////////
803
804 this->extendStorageIfNecessary();
805 master->extendStorageIfNecessary();
806
807 SoType mastertype = master->getTypeId();
808 SoType thistype = this->getTypeId();
809 SbBool containerisconverter = this->getContainer() &&
810 this->getContainer()->getTypeId().isDerivedFrom(SoFieldConverter::getClassTypeId());
811
812
813 // Set up all links. ///////////////////////////////////////////
814
815 if (mastertype == thistype) { // Can do direct field-to-field link.
816 if (!append) this->disconnect();
817 else if (this->storage->masterfields.find(master) >= 0) {
818 // detect and avoid multiple connections between the same fields
819 // (a common bug in VRML files created by 3ds max).
820 #if COIN_DEBUG
821 SoFieldContainer * fc = master->getContainer();
822 SbName fcname = fc ? fc->getName() : SbName::empty();
823 if (fcname != SbName::empty()) {
824 SbName fieldname;
825 (void) fc->getFieldName(master, fieldname);
826 SoDebugError::postWarning("SoField::connectFrom",
827 "connection from %p (%s.%s) already made",
828 master,
829 fcname.getString(),
830 fieldname.getString());
831
832 }
833 else {
834 SoDebugError::postWarning("SoField::connectFrom",
835 "connection from %p already made", master);
836 }
837 #endif // COIN_DEBUG
838 return FALSE;
839 }
840 // Set up the auditor link from the master to the slave field.
841 // (Note that the ``this'' slave field can also be an input field
842 // of an SoFieldConverter instance.)
843 master->addAuditor(this, SoNotRec::FIELD);
844 }
845 else { // Needs an SoFieldConverter between the fields.
846 SoFieldConverter * conv = this->createConverter(mastertype);
847 if (!conv) {
848 // Just return FALSE and don't bother to warn, as that is done
849 // by the createConverter() method.
850 return FALSE;
851 }
852
853 if (!append) this->disconnect();
854
855 SoField * converterinput = conv->getInput(mastertype);
856 SoEngineOutput * converteroutput = conv->getOutput(thistype);
857
858 #if COIN_DEBUG
859 if (converterinput == NULL) {
860 SoDebugError::post("SoField::connectFrom",
861 "input field returned from field converter is NULL");
862 return FALSE;
863 } else if (converteroutput == NULL) {
864 SoDebugError::post("SoField::connectFrom",
865 "output returned from field converter is NULL");
866 return FALSE;
867 }
868 #endif // COIN_DEBUG
869
870 // Link up the input SoField of the SoFieldConverter to the master
871 // field by recursively calling connectFrom().
872 // the converter engine should always be notified upon connection
873 // as it will never have a default value read in from a file.
874 converterinput->connectFrom(master, FALSE);
875
876 // Connect from the SoFieldConverter output to the slave field.
877 converteroutput->addConnection(this);
878
879 // Remember the connection from the slave field to the
880 // SoFieldConverter by setting up a dict entry.
881 this->storage->addConverter(master, conv);
882 }
883
884 // Common bookkeeping.
885 this->storage->masterfields.append(master); // slave -> master link
886 if (!containerisconverter)
887 master->storage->slaves.append(this); // master -> slave link
888
889
890 // Notification. ///////////////////////////////////////////////
891
892 if ((notnotify == FALSE) && this->isConnectionEnabled()) {
893 this->setDirty(TRUE);
894 this->setDefault(FALSE);
895 this->startNotify();
896 }
897
898 return TRUE;
899 }
900
901 /*!
902 Connects this field as a slave to \a master. This means that the value
903 of this field will be automatically updated when \a master is changed (as
904 long as the connection also is enabled).
905
906 If this field had any master-relationships beforehand, these are all
907 broken up if \a append is \c FALSE.
908
909 Call with \a notnotify if you want to avoid the initial notification
910 of connected auditors (a.k.a. \e slaves).
911
912 Function will return \c TRUE unless:
913
914 \li If the field output connected \e from is of a different type
915 from the engine output field-type connected \e to, a field
916 converter is inserted. For some combinations of fields no such
917 conversion is possible, and we'll return \c FALSE.
918
919 \li If this field is already connected to the \a master, we will
920 return \c FALSE.
921
922 \sa enableConnection(), isConnectionEnabled(), isConnectedFromField()
923 \sa getConnectedField(), appendConnection(SoEngineOutput *)
924 */
925 SbBool
connectFrom(SoEngineOutput * master,SbBool notnotify,SbBool append)926 SoField::connectFrom(SoEngineOutput * master, SbBool notnotify, SbBool append)
927 {
928 // Initialize. /////////////////////////////////////////////////
929
930 this->extendStorageIfNecessary();
931
932 SoType mastertype = master->getConnectionType();
933 SoType thistype = this->getTypeId();
934
935 // If we connectFrom() on the same engine as the field is already
936 // connected to, we want to avoid the master container engine being
937 // unref()'ed down to ref-count 0 upon the disconnect().
938 SoFieldContainer * masterengine = master->getFieldContainer();
939
940 if (masterengine) masterengine->ref();
941
942
943 // Set up all links. ///////////////////////////////////////////
944
945 if (mastertype == thistype) { // Can do direct field-to-engineout link.
946 if (!append) this->disconnect();
947 else {
948 // check if we're already connected
949 if (this->storage->masterengineouts.find(master) >= 0) {
950 // detect and avoid multiple connections between the same
951 // field and engine output (a common bug in VRML files
952 // created by 3ds max).
953 #if COIN_DEBUG
954 SoDebugError::postWarning("SoField::connectFrom",
955 "connection from %p already made", master);
956 #endif // COIN_DEBUG
957 // Match the ref() invocation.
958 if (masterengine) masterengine->unref();
959 return FALSE;
960 }
961 }
962 // Set up the auditor link from the master engineout to the slave
963 // field. (Note that the ``this'' slave field can also be an
964 // input field of an SoFieldConverter instance.)
965
966 // This is enough, the container SoEngine will automatically pick
967 // up on it.
968 master->addConnection(this);
969 }
970 else { // Needs an SoFieldConverter between this field and the SoEngineOutput
971 SoFieldConverter * conv = this->createConverter(mastertype);
972 if (!conv) { // Handle this exception.
973 // Clean up the ref().
974 if (masterengine) masterengine->unref();
975 // Sorry, can't connect. Don't bother to spit out a warning, as
976 // that is done in createConverter().
977 return FALSE;
978 }
979
980 if (!append) this->disconnect();
981
982 SoField * converterinput = conv->getInput(mastertype);
983 SoEngineOutput * converteroutput = conv->getOutput(thistype);
984
985 #if COIN_DEBUG
986 if (converterinput == NULL) {
987 SoDebugError::post("SoField::connectFrom",
988 "input field returned from field converter is NULL");
989 return FALSE;
990 } else if (converteroutput == NULL) {
991 SoDebugError::post("SoField::connectFrom",
992 "output returned from field converter is NULL");
993 return FALSE;
994 }
995 #endif // COIN_DEBUG
996
997 // Link up the input SoField of the SoFieldConverter to the master
998 // SoEngineOutput by recursively calling connectFrom().
999 // the converter engine should always be notified upon connection
1000 // as it will never have a default value read in from a file
1001 converterinput->connectFrom(master, FALSE);
1002
1003 // Connect from the SoFieldConverter output to the slave field.
1004 converteroutput->addConnection(this);
1005
1006 // Remember the connection from the slave field to the
1007 // SoFieldConverter by setting up a dict entry.
1008 this->storage->addConverter(master, conv);
1009 }
1010
1011 // Match the ref() invocation.
1012 if (masterengine) masterengine->unref();
1013
1014 // Common bookkeeping.
1015 this->storage->masterengineouts.append(master); // slave -> master link
1016
1017 // Notification. ///////////////////////////////////////////////
1018
1019 if ((notnotify == FALSE) && this->isConnectionEnabled()) {
1020 this->setDirty(TRUE);
1021 this->setDefault(FALSE);
1022 this->startNotify();
1023 }
1024
1025 return TRUE;
1026 }
1027
1028
1029 /*!
1030 Disconnect this field as a slave from \a master.
1031 */
1032 void
disconnect(SoField * master)1033 SoField::disconnect(SoField * master)
1034 {
1035 #if COIN_DEBUG_EXTRA
1036 int wLevel =
1037 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
1038 if (wLevel>=3)
1039 SoDebugError::postInfo("SoField::disconnect",
1040 "removing slave field %p from master field %p",
1041 this, master);
1042 #endif //COIN_DEBUG_EXTRA
1043
1044 const int idx = this->storage->masterfields.find(master);
1045 if (idx == -1) {
1046 SoDebugError::post("SoField::disconnect",
1047 "can't disconnect from a field which we're not connected to!");
1048 return;
1049 }
1050
1051 this->evaluate();
1052
1053 SbBool containerisconverter = this->getContainer() &&
1054 this->getContainer()->getTypeId().isDerivedFrom(SoFieldConverter::getClassTypeId());
1055
1056
1057 // Decouple links. ///////////////////////////////////////////////////
1058
1059 // Remove bookkeeping material.
1060 if (!containerisconverter) master->storage->slaves.removeItem(this);
1061
1062 this->storage->masterfields.remove(idx);
1063
1064 SoFieldConverter * converter = this->storage->findConverter(master);
1065 if (converter) { // There's a converter engine between the fields.
1066
1067 SoField * converterinput =
1068 converter->getInput(SoType::badType()); // dummy type
1069 converterinput->disconnect(master);
1070
1071 SoEngineOutput * converteroutput =
1072 converter->getOutput(SoType::badType()); // dummy type
1073 converteroutput->removeConnection(this);
1074
1075 this->storage->removeConverter(master);
1076 converter->unref();
1077 }
1078 else { // No converter, just a direct link.
1079 master->removeAuditor(this, SoNotRec::FIELD);
1080 }
1081
1082 // detect and unref() global fields. This is done to detect when the
1083 // last reference to a global fields is deleted.
1084 if (master->getContainer() && master->getContainer()->isOfType(SoGlobalField::getClassTypeId())) {
1085 master->getContainer()->unref();
1086 }
1087 }
1088
1089 /*!
1090 Disconnect this field as a slave from \a master.
1091 */
1092 void
disconnect(SoEngineOutput * master)1093 SoField::disconnect(SoEngineOutput * master)
1094 {
1095 // First check to see we're the input field of an
1096 // SoFieldConverter. If so, recursively call disconnect() with the
1097 // field on "the other side" of the converter.
1098
1099 SoType fieldconvtype = SoFieldConverter::getClassTypeId();
1100 SbBool containerisconverter =
1101 this->getContainer() &&
1102 this->getContainer()->getTypeId().isDerivedFrom(fieldconvtype);
1103 if (containerisconverter) {
1104 SoFieldConverter * converter =
1105 coin_assert_cast<SoFieldConverter *>(this->getContainer());
1106 SoEngineOutput * converterout =
1107 converter->getOutput(SoType::badType()); // dummy type
1108 SoFieldList fl;
1109 converterout->getForwardConnections(fl);
1110 fl[0]->disconnect(master);
1111 return;
1112 }
1113
1114
1115 #if COIN_DEBUG_EXTRA
1116 int wLevel =
1117 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
1118 if (wLevel>=3)
1119 SoDebugError::postInfo("SoField::disconnect",
1120 "removing slave field %p (%s.%s) from master "
1121 "engineout %p",
1122 this,
1123 this->storage->container->getTypeId().getName().getString(),
1124 this->storage->fieldtype.getName().getString(),
1125 master);
1126 #endif //COIN_DEBUG_EXTRA
1127
1128
1129 // Check the enabled flag to avoid evaluating from engines which are
1130 // being destructed. This is a bit of a hack, but I don't think it
1131 // matters. -- mortene.
1132 if (master->isEnabled()) this->evaluate();
1133
1134 // Decouple links. ///////////////////////////////////////////////////
1135
1136 // Remove bookkeeping material.
1137 this->storage->masterengineouts.removeItem(master);
1138
1139 SoFieldConverter * converter = this->storage->findConverter(master);
1140 if (converter) { // There's a converter engine between the fields.
1141 SoField * converterinput =
1142 converter->getInput(SoType::badType()); // dummy type
1143 converterinput->storage->masterengineouts.removeItem(master);
1144 master->removeConnection(converterinput);
1145
1146 SoEngineOutput * converteroutput =
1147 converter->getOutput(SoType::badType()); // dummy type
1148 converteroutput->removeConnection(this);
1149
1150 this->storage->removeConverter(master);
1151 converter->unref();
1152 }
1153 else { // No converter, just a direct link.
1154 master->removeConnection(this);
1155 }
1156 }
1157
1158 /*!
1159 Returns number of fields this field is a slave of.
1160
1161 \sa getConnections()
1162 */
1163 int
getNumConnections(void) const1164 SoField::getNumConnections(void) const
1165 {
1166 return this->hasExtendedStorage() ?
1167 this->storage->masterfields.getLength() : 0;
1168 }
1169
1170 /*!
1171 Returns number of masters this field is connected to, and places
1172 pointers to all of them into \a masterlist.
1173
1174 Note that we replace the contents of \a masterlist, i.e. we're \e
1175 not appending new data.
1176
1177 \sa getNumConnections()
1178 */
1179 int
getConnections(SoFieldList & masterlist) const1180 SoField::getConnections(SoFieldList & masterlist) const
1181 {
1182 if (!this->hasExtendedStorage()) return 0;
1183
1184 masterlist = this->storage->masterfields;
1185 return masterlist.getLength();
1186 }
1187
1188 /*!
1189 Disconnect all connections from this field as a slave to master
1190 fields or engine outputs.
1191 */
1192 void
disconnect(void)1193 SoField::disconnect(void)
1194 {
1195 // Disconnect us from all master fields.
1196 while (this->isConnectedFromField())
1197 this->disconnect(this->storage->masterfields[0]);
1198
1199 // Disconnect us from all master engine outputs.
1200 while (this->isConnectedFromEngine())
1201 this->disconnect(this->storage->masterengineouts[0]);
1202
1203 assert(this->isConnected() == FALSE);
1204 }
1205
1206 /*!
1207 Returns \c TRUE if we're connected from another field, engine or
1208 interpolator.
1209
1210 \sa isConnectedFromField(), isConnectedFromEngine()
1211 \sa connectFrom()
1212 */
1213 SbBool
isConnected(void) const1214 SoField::isConnected(void) const
1215 {
1216 return (this->isConnectedFromField() ||
1217 this->isConnectedFromEngine());
1218 }
1219
1220 /*!
1221 Returns \c TRUE if we're a slave of at least one field.
1222
1223 \sa isConnected(), isConnectedFromEngine()
1224 \sa connectFrom(SoField *)
1225 */
1226 SbBool
isConnectedFromField(void) const1227 SoField::isConnectedFromField(void) const
1228 {
1229 return (this->hasExtendedStorage() &&
1230 this->storage->masterfields.getLength() > 0);
1231 }
1232
1233 /*!
1234 Returns \c TRUE if we're connected from an engine.
1235
1236 \sa isConnected(), isConnectedFromField()
1237 \sa connectFrom(SoEngineOutput *)
1238 */
1239 SbBool
isConnectedFromEngine(void) const1240 SoField::isConnectedFromEngine(void) const
1241 {
1242 return (this->hasExtendedStorage() &&
1243 this->storage->masterengineouts.getLength() > 0);
1244 }
1245
1246 // Simplify by collecting common code for SoField::getConnected*() methods.
1247 #define SOFIELD_GETCONNECTED(_masterlist_) \
1248 if (!this->hasExtendedStorage()) return FALSE; \
1249 int nrmasters = this->storage->_masterlist_.getLength(); \
1250 if (nrmasters < 1) return FALSE; \
1251 master = this->storage->_masterlist_[nrmasters - 1]; \
1252 return TRUE
1253
1254 /*!
1255 Returns \c TRUE if we are connected as a slave to at least one other
1256 field. \a master will be set to the source field in the last field
1257 connection made.
1258
1259 \sa isConnectedFromField(), connectFrom(SoField *),
1260 \sa appendConnection(SoField *)
1261 */
1262 SbBool
getConnectedField(SoField * & master) const1263 SoField::getConnectedField(SoField *& master) const
1264 {
1265 SOFIELD_GETCONNECTED(masterfields);
1266 }
1267
1268 /*!
1269 Returns \c TRUE if we are connected as a slave to at least one
1270 engine. \a master will be set to the source of the last engine
1271 connection made.
1272
1273 \sa isConnectedFromEngine(), connectFrom(SoEngineOutput *)
1274 \sa appendConnection(SoEngineOutput *)
1275 */
1276 SbBool
getConnectedEngine(SoEngineOutput * & master) const1277 SoField::getConnectedEngine(SoEngineOutput *& master) const
1278 {
1279 SOFIELD_GETCONNECTED(masterengineouts);
1280 }
1281
1282 #undef SOFIELD_GETCONNECTED
1283
1284 /*!
1285 Appends all the fields which are auditing this field in \a
1286 slavelist, and returns the number of fields which are our slaves.
1287 */
1288 int
getForwardConnections(SoFieldList & slavelist) const1289 SoField::getForwardConnections(SoFieldList & slavelist) const
1290 {
1291 if (!this->hasExtendedStorage()) return 0;
1292
1293 int nr = 0;
1294
1295 for (int i=0; i < this->storage->slaves.getLength(); i++) {
1296 slavelist.append(this->storage->slaves[i]);
1297 nr++;
1298 }
1299
1300 return nr;
1301 }
1302
1303 /*!
1304 Let the field know to which container it belongs.
1305
1306 \sa getContainer(), SoFieldContainer
1307 */
1308 void
setContainer(SoFieldContainer * cont)1309 SoField::setContainer(SoFieldContainer * cont)
1310 {
1311 if (!this->hasExtendedStorage()) this->container = cont;
1312 else this->storage->container = cont;
1313
1314 // The field should have been set to its default value before it is
1315 // added to the container.
1316 //
1317 // This might seem strange, but it looks like it is necessary to do
1318 // it this way to be compatible with Open Inventor.
1319 this->setDefault(TRUE);
1320 }
1321
1322 /*!
1323 Returns the SoFieldContainer object "owning" this field.
1324
1325 \sa SoFieldContainer, setContainer()
1326 */
1327 SoFieldContainer *
getContainer(void) const1328 SoField::getContainer(void) const
1329 {
1330 if (!this->hasExtendedStorage()) return this->container;
1331 else return this->storage->container;
1332 }
1333
1334 /*!
1335 Set the field's value through the given \a valuestring. The format
1336 of the string must adhere to the ASCII format used in Coin data
1337 format files.
1338
1339 Only the value should be specified - \e not the name of the field.
1340
1341 \c FALSE is returned if the field value is invalid for the field
1342 type and can't be parsed in any sensible way.
1343
1344 \sa get()
1345 */
1346 SbBool
set(const char * valuestring)1347 SoField::set(const char * valuestring)
1348 {
1349 // Note that it is not necessary to set a header identification line
1350 // for this to work.
1351 SoInput in;
1352 in.setBuffer(const_cast<char *>(valuestring), strlen(valuestring));
1353 if (!this->readValue(&in)) return FALSE;
1354
1355 this->valueChanged();
1356 return TRUE;
1357 }
1358
1359 /*!
1360 Returns the field's value as an ASCII string in the export data
1361 format for Inventor files.
1362
1363 \sa set()
1364 */
1365 void
get(SbString & valuestring)1366 SoField::get(SbString & valuestring)
1367 {
1368 // FIXME: this function should be const! 20050607 mortene.
1369
1370 // NOTE: this code has an almost verbatim copy in SoMField::get1(),
1371 // so remember to update both places if any fixes are done.
1372
1373 // Initial buffer setup.
1374 SoOutput out;
1375 char initbuffer[SOFIELD_GET_STACKBUFFER_SIZE];
1376 char * bufferptr = NULL; // indicates that initial buffer is on the stack
1377
1378 CC_MUTEX_LOCK(sofield_mutex);
1379 SbBool ok = SoFieldP::getReallocHash()->put(initbuffer, &bufferptr);
1380 assert(ok);
1381 CC_MUTEX_UNLOCK(sofield_mutex);
1382
1383 out.setBuffer(initbuffer, sizeof(initbuffer), SoFieldP::hashRealloc);
1384
1385 // Record offset to skip header.
1386 out.write("");
1387 size_t offset;
1388 void * buffer;
1389 out.getBuffer(buffer, offset);
1390
1391 // Write field..
1392 out.setStage(SoOutput::COUNT_REFS);
1393 this->countWriteRefs(&out);
1394 out.setStage(SoOutput::WRITE);
1395 this->writeValue(&out);
1396
1397 // ..then read it back into the SbString.
1398 size_t size;
1399 out.getBuffer(buffer, size);
1400 valuestring = static_cast<char *>(buffer) + offset;
1401
1402 // dealloc tmp memory buffer
1403 if (bufferptr) { free(bufferptr); }
1404
1405 CC_MUTEX_LOCK(sofield_mutex);
1406 size_t isok = SoFieldP::getReallocHash()->erase(bufferptr ? bufferptr : initbuffer);
1407 assert(isok);
1408 CC_MUTEX_UNLOCK(sofield_mutex);
1409 }
1410
1411 /*!
1412 Notify the field as well as the field's owner / container that it
1413 has been changed.
1414
1415 Touching a field which is part of any component (engine or node) in
1416 a scene graph will lead to a forced redraw. This is useful if you
1417 have been doing several updates to the field wrapped in a pair of
1418 enableNotify() calls to notify the field's auditors that its value
1419 has changed.
1420
1421 \sa setValue(), enableNotify()
1422 */
1423 void
touch(void)1424 SoField::touch(void)
1425 {
1426 if (this->container) this->startNotify();
1427 }
1428
1429 /*!
1430 Trigger a notification sequence.
1431
1432 At the end of a notification sequence, all "immediate" sensors
1433 (i.e. sensors set up with a zero priority) are triggered.
1434 */
1435 void
startNotify(void)1436 SoField::startNotify(void)
1437 {
1438 SoNotList l;
1439 #if COIN_DEBUG_EXTRA
1440 int wLevel =
1441 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
1442 if (wLevel>=3)
1443 SoDebugError::postInfo("SoField::startNotify", "field %p (%s), list %p",
1444 this, this->getTypeId().getName().getString(), &l);
1445 #endif //COIN_DEBUG_EXTRA
1446
1447 SoDB::startNotify();
1448 this->notify(&l);
1449 SoDB::endNotify();
1450
1451 #if COIN_DEBUG_EXTRA
1452 if (wLevel>=3)
1453 SoDebugError::postInfo("SoField::startNotify", "DONE\n\n");
1454 #endif //COIN_DEBUG_EXTRA
1455 }
1456
1457 /*!
1458 Notify auditors that this field has changed.
1459 */
1460 void
notify(SoNotList * nlist)1461 SoField::notify(SoNotList * nlist)
1462 {
1463 assert((this->statusbits & FLAG_ALIVE_PATTERN) == FLAG_ALIVE_PATTERN);
1464 #if COIN_DEBUG
1465 if (this->getContainer()) {
1466 this->getContainer()->assertAlive();
1467 }
1468 #endif // COIN_DEBUG
1469
1470 // check NotRec type to find if the notification was from a
1471 // connection. If someone changes the field directly we should
1472 // just continue.
1473
1474 SoNotRec * rec = nlist->getLastRec();
1475 if (rec) {
1476 SoNotRec::Type t = nlist->getLastRec()->getType();
1477 if (t == SoNotRec::ENGINE || t == SoNotRec::FIELD) {
1478 if (this->hasExtendedStorage()) {
1479 this->storage->lastnotify = static_cast<void *>(rec->getBase());
1480 }
1481 // don't process the notification if we're notified from a
1482 // connection, and connection is disabled (through
1483 // enableConnection())
1484 if (!this->isConnectionEnabled()) return;
1485 }
1486 }
1487
1488 // In Inventor it is legal to have circular field connections. This
1489 // test stops the notification from entering into an infinite
1490 // recursion because of such connections. The flag is set/cleared
1491 // before/after progagating the notification.
1492 if (this->getStatus(FLAG_ISNOTIFIED)) return;
1493
1494 // needed because of the So[SF|MF]Node fields. When a node inside
1495 // such a field is changed we must mark the field as not default so
1496 // that SoWriteAction will export it. We can safely call
1497 // setDefault(FALSE) for other field types as well, since the only
1498 // other reason for entering here is if the field is connected from
1499 // an engine output or from another field.
1500 this->setDefault(FALSE);
1501
1502 #if COIN_DEBUG_EXTRA
1503 int wLevel =
1504 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
1505 if (wLevel>=3)
1506 if (this != SoDB::getGlobalField("realTime")) {
1507 SoDebugError::postInfo("SoField::notify", "%p (%s (%s '%s')) -- start",
1508 this,
1509 this->getTypeId().getName().getString(),
1510 this->getContainer() ? this->getContainer()->getTypeId().getName().getString() : "*none*",
1511 this->getContainer() ? this->getContainer()->getName().getString() : "*none*");
1512 }
1513 #endif //COIN_DEBUG_EXTRA
1514
1515 // If we're not the originator of the notification process, we need
1516 // to be marked dirty, as it means something we're connected to as a
1517 // slave has changed and our value needs to be updated.
1518 //
1519 // Note: don't try to "optimize" code here by moving the setDirty()
1520 // call down into the isNotifyEnabled() check, as setDirty()
1521 // _should_ happen if we're not the originator -- no matter what the
1522 // status of the notification enable flag is.
1523 if (nlist->getFirstRec()) this->setDirty(TRUE);
1524
1525 if (this->isNotifyEnabled()) {
1526 SoFieldContainer * cont = this->getContainer();
1527 this->setStatusBits(FLAG_ISNOTIFIED);
1528 SoNotRec rec(createNotRec(cont));
1529 nlist->append(&rec, this);
1530 nlist->setLastType(SoNotRec::CONTAINER); // FIXME: Not sure about this. 20000304 mortene.
1531
1532 #if COIN_DEBUG_EXTRA
1533 int wLevel =
1534 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
1535 if (wLevel>=3)
1536 SoDebugError::postInfo("SoField::notify",
1537 "field %p, list %p", this, nlist);
1538 #endif //COIN_DEBUG_EXTRA
1539
1540 if (this->hasExtendedStorage() && this->storage->auditors.getLength()) {
1541 // need to copy list first if we're going to notify the auditors
1542 SoNotList listcopy(*nlist);
1543 if (cont) cont->notify(nlist);
1544 this->notifyAuditors(&listcopy);
1545 }
1546 else {
1547 if (cont) cont->notify(nlist);
1548 }
1549 this->clearStatusBits(FLAG_ISNOTIFIED);
1550 }
1551
1552 #if COIN_DEBUG_EXTRA
1553 if (wLevel>=3)
1554 if (this != SoDB::getGlobalField("realTime")) {
1555 SoDebugError::postInfo("SoField::notify", "%p (%s (%s '%s')) -- done",
1556 this,
1557 this->getTypeId().getName().getString(),
1558 this->getContainer() ? this->getContainer()->getTypeId().getName().getString() : "*none*",
1559 this->getContainer() ? this->getContainer()->getName().getString() : "*none*");
1560 }
1561 #endif //COIN_DEBUG_EXTRA
1562 }
1563
1564 /*!
1565 This method sets whether notification will be propagated on changing
1566 the value of the field. The old value of the setting is returned.
1567
1568 \sa isNotifyEnabled()
1569 */
1570 SbBool
enableNotify(SbBool on)1571 SoField::enableNotify(SbBool on)
1572 {
1573 const SbBool old = this->getStatus(FLAG_DONOTIFY);
1574 (void) this->changeStatusBits(FLAG_DONOTIFY, on);
1575 return old;
1576 }
1577
1578 /*!
1579 This method returns whether notification of changes to the field
1580 value are propagated to the auditors.
1581
1582 \sa enableNotify()
1583 */
1584 SbBool
isNotifyEnabled(void) const1585 SoField::isNotifyEnabled(void) const
1586 {
1587 return this->getStatus(FLAG_DONOTIFY);
1588 }
1589
1590 // Makes an extended storage block on first connection.
1591 void
extendStorageIfNecessary(void)1592 SoField::extendStorageIfNecessary(void)
1593 {
1594 if (!this->hasExtendedStorage()) {
1595 this->storage = new SoConnectStorage(this->container, this->getTypeId());
1596 this->setStatusBits(FLAG_EXTSTORAGE);
1597 }
1598 }
1599
1600 /*!
1601 Add an auditor to the list. All auditors will be notified whenever
1602 this field changes its value(s).
1603 */
1604 void
addAuditor(void * f,SoNotRec::Type type)1605 SoField::addAuditor(void * f, SoNotRec::Type type)
1606 {
1607 this->extendStorageIfNecessary();
1608 this->storage->auditors.append(f, type);
1609 this->connectionStatusChanged(+1);
1610 }
1611
1612 /*!
1613 Remove an auditor from the list.
1614 */
1615 void
removeAuditor(void * f,SoNotRec::Type type)1616 SoField::removeAuditor(void * f, SoNotRec::Type type)
1617 {
1618 #if COIN_DEBUG_EXTRA
1619 int wLevel =
1620 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
1621 if (wLevel>=3)
1622 SoDebugError::postInfo("SoField::removeAuditor",
1623 "%p removing %p", this, f);
1624 #endif //COIN_DEBUG_EXTRA
1625
1626 assert(this->hasExtendedStorage());
1627 this->storage->auditors.remove(f, type);
1628 this->connectionStatusChanged(-1);
1629 }
1630
1631 /*!
1632 Checks for equality. Returns \c 0 if the fields are of different
1633 type or the field's value(s) are not equal.
1634 */
1635 int
operator ==(const SoField & f) const1636 SoField::operator ==(const SoField & f) const
1637 {
1638 return this->isSame(f);
1639 }
1640
1641 /*!
1642 Returns \c TRUE if the fields are of different type or has different
1643 value.
1644 */
1645 int
operator !=(const SoField & f) const1646 SoField::operator !=(const SoField & f) const
1647 {
1648 return !this->isSame(f);
1649 }
1650
1651 /*!
1652 Returns \c TRUE if it is necessary to write the field when dumping a
1653 scene graph. This needs to be done if the field is not default (it
1654 has been changed from its default value), if it's ignored, or if
1655 it's connected from another field or engine.
1656 */
1657 SbBool
shouldWrite(void) const1658 SoField::shouldWrite(void) const
1659 {
1660 #if COIN_DEBUG_EXTRA
1661 int wLevel =
1662 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
1663 if (wLevel>=3) {
1664 SbString finfo = SoFieldP::getDebugIdString(this);
1665 SoDebugError::postInfo("SoField::shouldWrite",
1666 "%s: isDefault==%d, isIgnored==%d, isConnected==%d",
1667 finfo.getString(), this->isDefault(),
1668 this->isIgnored(), this->isConnected());
1669 }
1670 #endif //COIN_DEBUG_EXTRA
1671
1672 if (!this->isDefault()) return TRUE;
1673 if (this->isIgnored()) return TRUE;
1674
1675 if (this->isConnected()) {
1676 #if 0 // disabled (was only needed for bidirectional connections in PROTOs)
1677 // I suspect this code was here only to make the bidirectional
1678 // connection hack in SoProto work. Connected PROTO instance
1679 // fields should be written even if they have the default value
1680 // (just like any other field). pederb, 2005-12-20
1681 SoFieldContainer * thecontainer = this->getContainer();
1682 if ( thecontainer != NULL &&
1683 thecontainer->isOfType(SoProtoInstance::getClassTypeId()) ) {
1684 // PROTO instance fields are usually connected, but we don't want to
1685 // write out PROTO instance fields that contain default values - they
1686 // will be hooked up and get the default value from the PROTO interface
1687 // when they are read in again later anyways. -- 20040115 larsa
1688 return FALSE;
1689 }
1690 #endif // disabled PROTO hack
1691 return TRUE;
1692 }
1693
1694 // SGI Inventor seems to test forward connections here also. We
1695 // consider this is bug, since this field should not write just
1696 // because some field is connected from this field. pederb.
1697
1698 return FALSE;
1699 }
1700
1701 /*!
1702 Called whenever another slave attaches or detaches itself to us. \a
1703 numconnections is the difference in number of connections made
1704 (i.e. if stuff is \e disconnected, \a numconnections will be a
1705 negative number).
1706
1707 The default method is empty. Override in subclasses if you want do
1708 something special on connections/deconnections.
1709 */
1710 void
connectionStatusChanged(int COIN_UNUSED_ARG (numconnections))1711 SoField::connectionStatusChanged(int COIN_UNUSED_ARG(numconnections))
1712 {
1713 }
1714
1715 /*!
1716 Returns \c TRUE if this field should not be written into at the
1717 moment the method is called.
1718
1719 This method is used internally in Coin during notification and
1720 evaluation processes, and should normally not be of interest to the
1721 application programmer.
1722 */
1723 SbBool
isReadOnly(void) const1724 SoField::isReadOnly(void) const
1725 {
1726 return this->getStatus(FLAG_READONLY);
1727 }
1728
1729 /*!
1730 This method is internally called after SoField::copyFrom() during
1731 scene graph copies, and should do the operations necessary for
1732 fixing up the field instance after it has gotten a new value.
1733
1734 The default method in the SoField superclass does nothing.
1735
1736 The application programmer should normally not need to consider this
1737 method, unless he constructs a complex field type which contains new
1738 references to container instances (i.e. nodes or
1739 engines). Overriding this method is then necessary to update the
1740 reference pointers, as they could have been duplicated during the
1741 copy operation.
1742 */
1743 void
fixCopy(SbBool COIN_UNUSED_ARG (copyconnections))1744 SoField::fixCopy(SbBool COIN_UNUSED_ARG(copyconnections))
1745 {
1746 }
1747
1748 /*!
1749 Returns \c TRUE if this field has references to any containers in
1750 the scene graph which are also duplicated during the copy operation.
1751
1752 Note that this method \e only is valid to call during copy
1753 operations.
1754
1755 See also the note about the relevance of the fixCopy() method for
1756 application programmers, as it is applicable on this method aswell.
1757 */
1758 SbBool
referencesCopy(void) const1759 SoField::referencesCopy(void) const
1760 {
1761 int i, n;
1762 if (!this->hasExtendedStorage()) return FALSE;
1763
1764 const SoFieldList & masterfields = this->storage->masterfields;
1765 n = masterfields.getLength();
1766 for (i = 0; i < n; i++) {
1767 SoFieldContainer * fc = masterfields[i]->getContainer();
1768 if (SoFieldContainer::checkCopy(fc)) return TRUE;
1769 }
1770
1771 const SoEngineOutputList & masterengineouts =
1772 this->storage->masterengineouts;
1773 n = masterengineouts.getLength();
1774 for (i = 0; i < n; i++) {
1775 SoEngineOutput * eout = masterengineouts[i];
1776 SbBool isengine = ! eout->isNodeEngineOutput();
1777 SoFieldContainer * fc = isengine ?
1778 coin_assert_cast<SoFieldContainer *>(eout->getContainer()) :
1779 coin_assert_cast<SoFieldContainer *>(eout->getNodeContainer());
1780 if (SoFieldContainer::checkCopy(fc)) return TRUE;
1781 if (isengine || (fc->isOfType(SoEngine::getClassTypeId()) &&
1782 coin_assert_cast<SoEngine *>(fc)->shouldCopy())) return TRUE;
1783 }
1784 return FALSE;
1785 }
1786
1787 /*!
1788 If \a fromfield contains a connection to another field, make this
1789 field also use the same connection.
1790 */
1791 void
copyConnection(const SoField * fromfield)1792 SoField::copyConnection(const SoField * fromfield)
1793 {
1794 // Consider most common case first.
1795 if (!fromfield->isConnected()) return;
1796
1797 // first, disconnect all existing connections (engines often
1798 // connect to the realTime global field in the constructor).
1799 this->disconnect();
1800
1801 assert(fromfield->hasExtendedStorage());
1802 int i;
1803
1804 for (i = 0; i < fromfield->storage->masterfields.getLength(); i++) {
1805 SoField * master = fromfield->storage->masterfields[i];
1806 SoFieldContainer * masterfc = master->getContainer();
1807 SbName fieldname;
1808 (void) masterfc->getFieldName(master, fieldname);
1809 SoFieldContainer * copyfc = masterfc->copyThroughConnection();
1810 SoField * copyfield = copyfc->getField(fieldname);
1811
1812 SbBool notnotify = FALSE;
1813 switch (master->getFieldType()) {
1814 case EVENTIN_FIELD:
1815 case EVENTOUT_FIELD:
1816 notnotify = TRUE;
1817 break;
1818 default:
1819 break;
1820 }
1821 (void) this->connectFrom(copyfield, notnotify, TRUE);
1822 }
1823 for (i = 0; i < fromfield->storage->masterengineouts.getLength(); i++) {
1824 SoEngineOutput * master = fromfield->storage->masterengineouts[i];
1825 SoEngineOutput * copyeo = NULL;
1826
1827 if (master->isNodeEngineOutput()) {
1828 SbName name;
1829 SoNodeEngine * masterengine = master->getNodeContainer();
1830 (void) masterengine->getOutputName(master, name);
1831 SoNodeEngine * copyengine =
1832 coin_assert_cast<SoNodeEngine *>(masterengine->copyThroughConnection());
1833 copyeo = copyengine->getOutput(name);
1834 }
1835 else {
1836 SbName name;
1837 SoEngine * masterengine = master->getContainer();
1838 (void) masterengine->getOutputName(master, name);
1839 SoEngine * copyengine =
1840 coin_assert_cast<SoEngine *>(masterengine->copyThroughConnection());
1841 copyeo = copyengine->getOutput(name);
1842 }
1843 assert(copyeo);
1844 (void) this->connectFrom(copyeo, FALSE, TRUE);
1845 }
1846 }
1847
1848 // This templatized inline is just a convenience function for reading
1849 // with error detection.
1850 template <class Type>
1851 static inline SbBool
READ_VAL(SoInput * in,Type & val)1852 READ_VAL(SoInput * in, Type & val)
1853 {
1854 if (!in->read(val)) {
1855 SoReadError::post(in, "Premature end of file");
1856 return FALSE;
1857 }
1858 return TRUE;
1859 }
1860
1861
1862 /*!
1863 Reads and sets the value of this field from the given SoInput
1864 instance. Returns \c FALSE if the field value can not be parsed
1865 from the input.
1866
1867 The second argument is the field's context-specific \a name, which
1868 is typically its unique identifier in its field container.
1869
1870 \sa set(), write()
1871 */
1872 SbBool
read(SoInput * in,const SbName & name)1873 SoField::read(SoInput * in, const SbName & name)
1874 {
1875 SbBool readok = TRUE;
1876 SbBool oldnotify = this->enableNotify(FALSE);
1877 SbBool didreadvalue = FALSE;
1878
1879 if (in->checkISReference(this->getContainer(), name, readok) || readok == FALSE) {
1880 if (!readok) {
1881 SoFieldContainer * fc = this->getContainer();
1882 SbString s("");
1883 if (fc) { s.sprintf(" of %s", fc->getTypeId().getName().getString()); }
1884 SoReadError::post(in, "Couldn't read value for field \"%s\"%s",
1885 name.getString(), s.getString());
1886 }
1887 goto sofield_read_return;
1888 }
1889
1890 this->setDefault(FALSE);
1891 this->setDirty(FALSE);
1892
1893 if (!in->isBinary()) { // ASCII file format.
1894 char c;
1895 // Check for the ignored flag first, as it is valid to let the
1896 // field data be just the ignored flag and nothing else.
1897 if (!READ_VAL(in, c)) { readok = FALSE; goto sofield_read_return; }
1898
1899 if (c == IGNOREDCHAR) this->setIgnored(TRUE);
1900 else {
1901 // First check if there's a field-to-field connection here as
1902 // the default value following the field can be omitted.
1903 if (c == CONNECTIONCHAR) {
1904 // There's potential for an obscure bug to happen here: if the
1905 // field is an SoSFString where the string is unquoted and
1906 // starts with a CONNECTIONCHAR (i.e. '='), it will lead to a
1907 // false positive for the if()-check below, which again causes a
1908 // rather obtuse error message:
1909 //
1910 // Coin read error: Expected '{'; got '}'
1911 // Occurred at line 3 in hepp.iv
1912 //
1913 // For the following test file:
1914 //
1915 // ----8<---- [snip] -------8<----
1916 // #Inventor V2.1 ascii
1917 //
1918 // Info { string =moo }
1919 // ----8<---- [snip] -------8<----
1920 //
1921 // Tamer Fahmy investigated and found this behavior to also
1922 // happen for SGI Inventor. Since that is the case, we won't
1923 // try to handle this as a valid file construct.
1924 //
1925 // FIXME: it would be nice if we could improve the error
1926 // message, to let the app programmer actually stand a chance
1927 // of debugging this when it happens. 20030811 mortene.
1928 if (!this->readConnection(in)) { readok = FALSE; goto sofield_read_return; }
1929 goto sofield_read_return;
1930 }
1931 else in->putBack(c);
1932
1933 // Read field value(s).
1934 if (!this->readValue(in)) {
1935 SoFieldContainer * fc = this->getContainer();
1936 SbString s("");
1937 if (fc) { s.sprintf(" of %s", fc->getTypeId().getName().getString()); }
1938 SoReadError::post(in, "Couldn't read value for field \"%s\"%s",
1939 name.getString(), s.getString());
1940 readok = FALSE;
1941 goto sofield_read_return;
1942 }
1943 else didreadvalue = TRUE;
1944
1945 // Check again for the ignored flag indicator after the field
1946 // value.
1947 if (in->read(c)) { // if-check in case EOF on an SoField::set() invocation
1948 if (c == IGNOREDCHAR) this->setIgnored(TRUE);
1949 else in->putBack(c);
1950 }
1951 }
1952
1953 // Check field-to-field connection indicator again /after/ the
1954 // field (start-)value.
1955 if (in->read(c)) { // if-check in case EOF on an SoField::set() invocation
1956 if (c == CONNECTIONCHAR) { if (!this->readConnection(in)) { readok = FALSE; goto sofield_read_return; } }
1957 else { in->putBack(c); }
1958 }
1959 }
1960 else { // Binary file format.
1961 // Read field value(s).
1962 if (!this->readValue(in)) {
1963 SoFieldContainer * fc = this->getContainer();
1964 SbString s("");
1965 if (fc) { s.sprintf(" of %s", fc->getTypeId().getName().getString()); }
1966 SoReadError::post(in, "Couldn't read value for field \"%s\"%s",
1967 name.getString(), s.getString());
1968 readok = FALSE;
1969 goto sofield_read_return;
1970 }
1971 else didreadvalue = TRUE;
1972
1973 // Check for the "ignored", "connection" and "default" flags.
1974 unsigned int flags;
1975 if (!READ_VAL(in, flags)) { readok = FALSE; goto sofield_read_return; }
1976
1977 if (flags & SoField::IGNORED) this->setIgnored(TRUE);
1978 if (flags & SoField::CONNECTED) { if (!this->readConnection(in)) { readok = FALSE; goto sofield_read_return; }}
1979 if (flags & SoField::DEFAULT) this->setDefault(TRUE);
1980
1981 #if COIN_DEBUG
1982 if (flags & ~SoField::ALLFILEFLAGS) {
1983 SoDebugError::postWarning("SoField::read",
1984 "unknown field flags (0x%x) -- "
1985 "please report to <coin-support@coin3d.org>",
1986 flags);
1987 }
1988 #endif // COIN_DEBUG
1989 }
1990
1991 sofield_read_return:
1992 (void) this->enableNotify(oldnotify);
1993
1994 if (readok) {
1995 if (didreadvalue) this->valueChanged(FALSE);
1996 else {
1997 // we called setDirty(FALSE) in the beginning of the function.
1998 // Since this field is read without a value (just connected to
1999 // some other field/engine), we need to mark the field as dirty
2000 // so that it's evaluated the next time the field is read
2001 this->setDirty(TRUE);
2002 this->startNotify();
2003 }
2004 }
2005 return readok;
2006 }
2007
2008 /*!
2009 Write the value of the field to the given SoOutput instance (which
2010 can be either a memory buffer or a file, in ASCII or in binary
2011 format).
2012
2013 \sa get(), read(), SoOutput
2014 */
2015 void
write(SoOutput * out,const SbName & name) const2016 SoField::write(SoOutput * out, const SbName & name) const
2017 {
2018 if (out->getStage() == SoOutput::COUNT_REFS) {
2019 // Handle first stage of write operations.
2020 this->countWriteRefs(out);
2021 return;
2022 }
2023
2024 // Ok, we've passed the first write stage and is _really_ writing.
2025
2026 // Check connection (this is common code for ASCII and binary
2027 // write).
2028 SbBool writeconnection = FALSE;
2029 SbName dummy;
2030 SoFieldContainer * fc = this->resolveWriteConnection(dummy);
2031
2032 if (fc && (SoWriterefCounter::instance(out)->shouldWrite(fc) || fc->isOfType(SoEngine::getClassTypeId())))
2033 writeconnection = TRUE;
2034
2035 // check VRML2 connections. Since VRML2 fields can have multiple
2036 // master fields/engines, the field can still be default even though
2037 // it is connected, and we should _not_ write the field. The ROUTEs
2038 // should be added though. pederb, 2002-06-13
2039
2040 if (is_vrml2_field(this)) {
2041 if (writeconnection) {
2042 writeconnection = FALSE;
2043 this->storage->add_vrml2_routes(out, this);
2044 // if no value has been set, don't write field even if it's
2045 // connected
2046 if (this->isDefault()) return;
2047 }
2048 // never write eventIn or eventOut fields
2049 if ((this->getFieldType() == SoField::EVENTIN_FIELD) ||
2050 (this->getFieldType() == SoField::EVENTOUT_FIELD)) return;
2051 }
2052
2053 // ASCII write.
2054 if (!out->isBinary()) {
2055 out->indent();
2056 // Cast to avoid "'s.
2057 out->write(static_cast<const char *>(name));
2058 if (!this->isDefault()) {
2059 out->write(' ');
2060 this->writeValue(out);
2061 }
2062 if (this->isIgnored()) {
2063 out->write(' ');
2064 out->write(IGNOREDCHAR);
2065 }
2066
2067 if (writeconnection) this->writeConnection(out);
2068 out->write('\n');
2069 }
2070 // Binary write.
2071 else {
2072 // Cast to avoid "'s.
2073 out->write(static_cast<const char *>(name));
2074 this->writeValue(out);
2075
2076 unsigned int flags = 0;
2077 if (this->isIgnored()) flags |= SoField::IGNORED;
2078 if (writeconnection) flags |= SoField::CONNECTED;
2079 if (this->isDefault()) flags |= SoField::DEFAULT;
2080
2081 out->write(flags);
2082
2083 if (writeconnection) this->writeConnection(out);
2084 }
2085 }
2086
2087 #include <cstdio>
2088
2089 /*!
2090 This method is called during the first pass of write operations, to
2091 count the number of write references to this field and to "forward"
2092 the reference counting operation to the field containers we're
2093 connected to.
2094 */
2095 void
countWriteRefs(SoOutput * out) const2096 SoField::countWriteRefs(SoOutput * out) const
2097 {
2098 // Count all connected fields/engines. Inventor only allows one
2099 // master field/engine, but VRML2 can have multiple. This code
2100 // should work for both Inventor and VRML2 scene graphs
2101 // though. pederb, 2002-06-13
2102 if (this->isConnected()) {
2103 if (is_vrml2_field(this)) {
2104 this->storage->add_vrml2_routes(out, this);
2105 }
2106 else {
2107 int i;
2108 for (i = 0; i < this->storage->masterfields.getLength(); i++) {
2109 SoField * master = this->storage->masterfields[i];
2110 SoFieldContainer * fc = master->getContainer();
2111 assert(fc);
2112 // TRUE = reference is from field connection. This is needed
2113 // so that the fields inside 'fc' is counted only once
2114 fc->addWriteReference(out, TRUE);
2115 }
2116 for (i = 0; i < this->storage->masterengineouts.getLength(); i++) {
2117 SoEngineOutput * engineout = this->storage->masterengineouts[i];
2118 SoFieldContainer * fc = engineout->getFieldContainer();
2119 assert(fc);
2120 // since engines are always connected directly to the field
2121 // (they're not nodes), engines are always counted with
2122 // isfromfield = FALSE
2123 fc->addWriteReference(out, FALSE);
2124 }
2125 }
2126 }
2127 }
2128
2129 /*!
2130 \fn void SoField::evaluate(void) const
2131
2132 Re-evaluates the value of this field any time a getValue() call is
2133 made and the field is marked dirty. This is done in this way to gain
2134 the advantages of having lazy evaluation.
2135 */
2136
2137 //
2138 // private method called from SoField::evaluate() when the field is
2139 // connected and dirty
2140 //
2141 void
evaluateField(void) const2142 SoField::evaluateField(void) const
2143 {
2144 // if we're destructing, don't continue as this would cause
2145 // a call to the virtual evaluateConnection()
2146 if (this->getStatus(FLAG_ISDESTRUCTING)) {
2147 #if COIN_DEBUG_EXTRA
2148 int wLevel =
2149 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
2150 if (wLevel>=3) {
2151 SoDebugError::postInfo("SoField::evaluate",
2152 "Stopped evaluate while destructing.");
2153 }
2154 #endif //COIN_DEBUG_EXTRA
2155 return;
2156 }
2157
2158 if (!this->isConnected()) return;
2159
2160 assert(this->storage != NULL);
2161
2162 // lock _before_ testing FLAG_ISEVALUATING to be thread safe
2163 SOFIELD_RECLOCK;
2164 // Recursive calls to SoField::evalute() should _absolutely_ not
2165 // happen, as the state of the field variables might not be
2166 // consistent while evaluating.
2167 //
2168 // This is an error which is not too hard to bump into, and the
2169 // _immediate_ repercussions are non-fatal, so we just spit out this
2170 // error message and carry on -- to not cause any more application
2171 // programmer frustrations than necessary.
2172
2173 if (this->getStatus(FLAG_ISEVALUATING)) {
2174 #if COIN_DEBUG
2175 SoDebugError::post("SoField::evaluate",
2176 "Detected indirectly recursive call to "
2177 "SoField::evaluate() -- which is a *bad* thing."
2178 "This indicates a non-trivial programming error "
2179 "somewhere either in the application (most likely) "
2180 "or the library code itself (less likely). "
2181 "We strongly advise you to investigate and resolve "
2182 "this issue before moving on.");
2183 #endif // COIN_DEBUG
2184 SOFIELD_RECUNLOCK;
2185 return;
2186 }
2187
2188 // Cast away the const. (evaluate() must be const, since we're using
2189 // evaluate() from getValue().)
2190 SoField * that = const_cast<SoField *>(this);
2191
2192 // Check the NEEDEVALUATION flag in case some other thread has just
2193 // evaluated the field. The flag is checked in SoField::evaluate(),
2194 // but it is possible that two (or more) threads might enter
2195 // evaluateField() simultaneously, so this test is necessary.
2196 // pederb, 2002-10-04
2197 if (this->getStatus(FLAG_NEEDEVALUATION) && this->getStatus(FLAG_ENABLECONNECTS)) {
2198 that->setStatusBits(FLAG_ISEVALUATING);
2199 this->evaluateConnection();
2200 that->clearStatusBits(FLAG_ISEVALUATING);
2201 // this will clear the NEEDEVALUATION flag
2202 that->setDirty(FALSE);
2203 }
2204 SOFIELD_RECUNLOCK;
2205 }
2206
2207 /*!
2208 Do we need re-evaluation?
2209 */
2210 SbBool
getDirty(void) const2211 SoField::getDirty(void) const
2212 {
2213 return this->getStatus(FLAG_NEEDEVALUATION);
2214 }
2215
2216 /*!
2217 Mark field for re-evaluation (upon next read operation), but do not
2218 trigger a notification.
2219 */
2220 void
setDirty(SbBool dirty)2221 SoField::setDirty(SbBool dirty)
2222 {
2223 (void) this->changeStatusBits(FLAG_NEEDEVALUATION, dirty);
2224 }
2225
2226 /*!
2227 Connect ourself as slave to another object, while still keeping the
2228 other connections currently in place.
2229
2230 \sa connectFrom()
2231 */
2232 SbBool
appendConnection(SoEngineOutput * master,SbBool notnotify)2233 SoField::appendConnection(SoEngineOutput * master, SbBool notnotify)
2234 {
2235 return this->connectFrom(master, notnotify, TRUE);
2236 }
2237
2238 /*!
2239 Connect ourself as slave to another object, while still keeping the
2240 other connections currently in place.
2241
2242 \sa connectFrom()
2243 */
2244 SbBool
appendConnection(SoField * master,SbBool notnotify)2245 SoField::appendConnection(SoField * master, SbBool notnotify)
2246 {
2247 return this->connectFrom(master, notnotify, TRUE);
2248 }
2249
2250 // Make a converter from value(s) of the given field type and the
2251 // value(s) of this type. Returns NULL if no value conversion between
2252 // types is possible.
2253 SoFieldConverter *
createConverter(SoType from) const2254 SoField::createConverter(SoType from) const
2255 {
2256 SoType thistype = this->getTypeId();
2257 assert(from != thistype);
2258 SoType convtype = SoDB::getConverter(from, thistype);
2259 if (convtype == SoType::badType()) {
2260 #if COIN_DEBUG // COIN_DEBUG
2261 SoDebugError::postWarning("SoField::createConverter",
2262 "no converter for %s to %s",
2263 from.getName().getString(),
2264 thistype.getName().getString());
2265 #endif // COIN_DEBUG
2266 return NULL;
2267 }
2268
2269 SoFieldConverter * fc;
2270
2271 if (convtype == SoConvertAll::getClassTypeId())
2272 fc = new SoConvertAll(from, this->getTypeId());
2273 else
2274 fc = static_cast<SoFieldConverter *>(convtype.createInstance());
2275
2276 fc->ref();
2277 return fc;
2278 }
2279
2280
2281 /*!
2282 Read the master field of a field-to-field connection (and its field
2283 container).
2284
2285 If input parsing is successful, this field will be connected as a
2286 slave to the master field.
2287
2288 Note that this slave field will \e not be marked as "dirty" upon
2289 connection, i.e. it will retain its value until the first update of
2290 the master field is made \e after the connection was set up. This to
2291 be in conformance with how the Inventor Mentor specifies how field
2292 connections should be imported (see page 270).
2293 */
2294 SbBool
readConnection(SoInput * in)2295 SoField::readConnection(SoInput * in)
2296 {
2297 // For debugging purposes, here's a handy test case for checking
2298 // that a field-field connection, where an initial value for the
2299 // slave is given, will behave according to the Mentor, as mentioned
2300 // above in the function API documentation:
2301 //
2302 // -----8<------- [snip] -----------------8<------- [snip] -----------
2303 // #Inventor V2.1 ascii
2304 //
2305 // DEF SCENE_ROOT Separator {
2306 // ## on startup this should give a green cube
2307 // Switch {
2308 // whichChild 0 = SelectOne { type SoMFInt32 index 1 input [ 0,1 ] }.output
2309 // Material { diffuseColor 0.1 1.0 0.1 }
2310 // Material { diffuseColor 1.0 0.1 0.1 }
2311 // }
2312 // Cone {}
2313 // } # SCENE_ROOT
2314 // -----8<------- [snip] -----------------8<------- [snip] -----------
2315 //
2316 // (Provided by Gerhard Reitmayr.)
2317
2318 // ***********************************************************************
2319
2320 // Read the fieldcontainer instance containing the master field
2321 // we're connected to.
2322 SoBase * bp;
2323 if (!SoBase::read(in, bp, SoFieldContainer::getClassTypeId())) return FALSE;
2324 if (!bp) {
2325 SoReadError::post(in, "couldn't read field-to-field connection");
2326 return FALSE;
2327 }
2328
2329 SoFieldContainer * fc = coin_assert_cast<SoFieldContainer *>(bp);
2330
2331 // Scan past the '.' character for ASCII format.
2332 if (!in->isBinary()) {
2333 char c;
2334 if (!in->read(c)) {
2335 SoReadError::post(in, "premature EOF");
2336 return FALSE;
2337 }
2338 if (c != '.') {
2339 SoReadError::post(in, "expected field connection token '.', "
2340 "but got '%c'", c);
2341 return FALSE;
2342 }
2343 }
2344
2345 // Read name of master field.
2346 SbName mastername;
2347 if (!in->read(mastername, TRUE)) {
2348 SoReadError::post(in, "premature EOF");
2349 return FALSE;
2350 }
2351
2352 // Get pointer to master field or engine output and connect.
2353
2354 SoEngineOutput * masteroutput = NULL;
2355 SoField * masterfield = fc->getField(mastername);
2356
2357 if (!masterfield) {
2358 masteroutput =
2359 fc->isOfType(SoEngine::getClassTypeId()) ?
2360 coin_safe_cast<SoEngine*>(fc)->getOutput(mastername) : NULL;
2361
2362 if (!masteroutput) {
2363 masteroutput =
2364 fc->isOfType(SoNodeEngine::getClassTypeId()) ?
2365 coin_safe_cast<SoNodeEngine *>(fc)->getOutput(mastername) : NULL;
2366 }
2367 }
2368
2369 if (!masterfield && !masteroutput) {
2370 SoReadError::post(in, "no field or output ``%s'' in ``%s''",
2371 mastername.getString(),
2372 fc->getTypeId().getName().getString());
2373 return FALSE;
2374 }
2375
2376 SbBool ok = FALSE;
2377
2378 // Make connection, with "do not notify" flag set to TRUE, to avoid
2379 // making ourselves "dirty" (i.e.: we will continue using our
2380 // current value until the master is updated).
2381 if (masterfield) { ok = this->connectFrom(masterfield, TRUE); }
2382 else if (masteroutput) { ok = this->connectFrom(masteroutput, TRUE); }
2383
2384 if (!ok) {
2385 SoReadError::post(in, "couldn't connect ``%s'' field to ``%s'', "
2386 "connection will be ignored",
2387 this->getTypeId().getName().getString(),
2388 mastername.getString());
2389 }
2390
2391 return TRUE;
2392 }
2393
2394 /*!
2395 Write out information about this field's connection.
2396 */
2397 void
writeConnection(SoOutput * out) const2398 SoField::writeConnection(SoOutput * out) const
2399 {
2400 SbName mastername;
2401 SoFieldContainer * fc = this->resolveWriteConnection(mastername);
2402 assert(fc);
2403
2404 if (!out->isBinary()) {
2405 out->write(' ');
2406 out->write(CONNECTIONCHAR);
2407 }
2408
2409 if (fc->isOfType(SoNode::getClassTypeId())) {
2410 SoWriteAction wa(out);
2411 wa.continueToApply(coin_assert_cast<SoNode *>(fc));
2412 }
2413 else {
2414 // Note: for this to work, classes inheriting SoFieldContainer
2415 // which are _not_ also inheriting from SoNode must call
2416 // SoBase::writeHeader() and SoBase::writeFooter().
2417 fc->writeInstance(out);
2418 // FIXME: does this work for engines? 20000131 mortene.
2419 }
2420
2421 if (!out->isBinary()) {
2422 out->indent();
2423 out->write(". ");
2424 }
2425
2426 out->write(mastername.getString());
2427 }
2428
2429 // Check if this field should write a connection upon export. Returns
2430 // a pointer to the fieldcontainer with the master field we're
2431 // connected to (or NULL if none, or if the master field's container
2432 // is not within the scenegraph). If the return value is non-NULL, the
2433 // name of the master field is copied to the mastername argument.
2434 SoFieldContainer *
resolveWriteConnection(SbName & mastername) const2435 SoField::resolveWriteConnection(SbName & mastername) const
2436 {
2437 if (!this->isConnected()) return NULL;
2438
2439 SoFieldContainer * fc = NULL;
2440 SoField * fieldmaster;
2441 SoEngineOutput * enginemaster;
2442
2443 if (this->getConnectedField(fieldmaster)) {
2444 fc = fieldmaster->getContainer();
2445 assert(fc);
2446 SbBool ok = fc->getFieldName(fieldmaster, mastername);
2447 assert(ok);
2448 }
2449 else if (this->getConnectedEngine(enginemaster)) {
2450 fc = enginemaster->getFieldContainer();
2451 assert(fc);
2452 // FIXME: couldn't we use getFieldName()? 20000129 mortene.
2453 SbBool ok =
2454 enginemaster->isNodeEngineOutput() ?
2455 coin_assert_cast<SoNodeEngine *>(fc)->getOutputName(enginemaster, mastername) :
2456 coin_assert_cast<SoEngine *>(fc)->getOutputName(enginemaster, mastername);
2457 assert(ok);
2458 }
2459 else assert(FALSE);
2460
2461 return fc;
2462 }
2463
2464
2465 /*!
2466 If we're connected to a field/engine/interpolator, copy the value
2467 from the master source.
2468 */
2469 void
evaluateConnection(void) const2470 SoField::evaluateConnection(void) const
2471 {
2472 SbBool fanin = this->storage->hasFanIn();
2473 SbBool didevaluate = FALSE;
2474
2475 // FIXME: should we evaluate from all masters in turn? 19990623 mortene.
2476 if (this->isConnectedFromField()) {
2477 int idx = fanin ? this->storage->findFanInField() : this->storage->masterfields.getLength() - 1;
2478 if (idx >= 0) {
2479 didevaluate = TRUE;
2480 SoField * master = this->storage->masterfields[idx];
2481 // don't copy if master is destructing, or if master is currently
2482 // evaluating. The master might be evaluating if we have circular
2483 // field connections. If this is the case, the field will already
2484 // contain the correct value, and we should not copy again.
2485 if (!master->isDestructing() && !master->getStatus(FLAG_ISEVALUATING)) {
2486 SoFieldConverter * converter = this->storage->findConverter(master);
2487 if (converter) converter->evaluateWrapper();
2488 else {
2489 SoField * that = const_cast<SoField *>(this); // cast away const
2490 // Copy data. Disable notification first since notification
2491 // has already been sent from the master.
2492 SbBool oldnotify = that->enableNotify(FALSE);
2493 that->copyFrom(*master);
2494 (void) that->enableNotify(oldnotify);
2495 }
2496 }
2497 }
2498 }
2499 if (this->isConnectedFromEngine() && !didevaluate) {
2500 int idx = fanin ? this->storage->findFanInEngine() : this->storage->masterengineouts.getLength() - 1;
2501 if (idx >= 0) {
2502 SoEngineOutput * master = this->storage->masterengineouts[idx];
2503 SoFieldConverter * converter = this->storage->findConverter(master);
2504 if (converter) converter->evaluateWrapper();
2505 else if (master->isNodeEngineOutput()) {
2506 master->getNodeContainer()->evaluateWrapper();
2507 }
2508 else {
2509 master->getContainer()->evaluateWrapper();
2510 }
2511 }
2512 }
2513 }
2514
2515 /*!
2516 This method is always called whenever the field's value has been
2517 changed by direct invocation of setValue() or some such. You should
2518 \e never call this method from anywhere in the code where the field
2519 value is being set through an evaluation of its connections.
2520
2521 If \a resetdefault is \c TRUE, the flag marking whether or not the
2522 field has its default value will be set to \c FALSE.
2523
2524 The method will also notify any auditors that the field's value has
2525 changed.
2526 */
2527 void
valueChanged(SbBool resetdefault)2528 SoField::valueChanged(SbBool resetdefault)
2529 {
2530 if (this->changeStatusBits(FLAG_READONLY, TRUE)) {
2531 this->setDirty(FALSE);
2532 if (resetdefault) this->setDefault(FALSE);
2533 if (this->container) this->startNotify();
2534 this->clearStatusBits(FLAG_READONLY);
2535 }
2536 }
2537
2538 // Notify any auditors by marking them dirty - i.e. ready for
2539 // re-evaluation. Auditors include connected fields, sensors,
2540 // containers (nodes/engines), ...
2541 void
notifyAuditors(SoNotList * l)2542 SoField::notifyAuditors(SoNotList * l)
2543 {
2544 #if COIN_DEBUG_EXTRA
2545 int wLevel =
2546 SoConfigSettings::getInstance()->settingAsInt("COIN_WARNING_LEVEL");
2547 if (wLevel>=3)
2548 SoDebugError::postInfo("SoField::notifyAuditors",
2549 "field %p, list %p", this, l);
2550 #endif //COIN_DEBUG_EXTRA
2551 if (this->hasExtendedStorage() && this->storage->auditors.getLength())
2552 this->storage->auditors.notify(l);
2553 }
2554
2555 /*!
2556 Set type of this field.
2557
2558 The possible values for \a type is: 0 for ordinary fields, 1 for
2559 eventIn fields, 2 for eventOut fields, 3 for internal fields, 4 for
2560 VRML2 exposedField fields. There are also enum values in SoField.h.
2561 */
2562 void
setFieldType(int type)2563 SoField::setFieldType(int type)
2564 {
2565 this->clearStatusBits(FLAG_TYPEMASK);
2566 assert(type >=0 && type <= FLAG_TYPEMASK);
2567 this->setStatusBits(static_cast<unsigned int>(type));
2568 }
2569
2570 /*!
2571 Return the type of this field.
2572
2573 \sa setFieldType()
2574 */
2575 int
getFieldType(void) const2576 SoField::getFieldType(void) const
2577 {
2578 return this->statusbits & FLAG_TYPEMASK;
2579 }
2580
2581 /*!
2582 Can be used to check if a field is being destructed.
2583 */
2584 SbBool
isDestructing(void) const2585 SoField::isDestructing(void) const
2586 {
2587 return this->getStatus(FLAG_ISDESTRUCTING);
2588 }
2589
2590 /*!
2591 \internal
2592 */
2593 SoNotRec
createNotRec(SoBase * cont)2594 SoField::createNotRec(SoBase * cont)
2595 {
2596 SoNotRec rec(cont);
2597 rec.setOperationType(SoNotRec::FIELD_UPDATE);
2598 return rec;
2599 }
2600
2601 /*!
2602 Initialize all the field classes.
2603 */
2604 void
initClasses(void)2605 SoField::initClasses(void)
2606 {
2607 SoSField::initClass();
2608 SoSFBox2s::initClass();
2609 SoSFBox2i32::initClass();
2610 SoSFBox2f::initClass();
2611 SoSFBox2d::initClass();
2612 SoSFBox3s::initClass();
2613 SoSFBox3i32::initClass();
2614 SoSFBox3f::initClass();
2615 SoSFBox3d::initClass();
2616 SoSFBool::initClass();
2617 SoSFColor::initClass();
2618 SoSFColorRGBA::initClass();
2619 SoSFDouble::initClass();
2620 SoSFEngine::initClass();
2621 SoSFFloat::initClass();
2622 SoSFShort::initClass();
2623 SoSFUShort::initClass();
2624 SoSFInt32::initClass();
2625 SoSFUInt32::initClass();
2626 SoSFVec2b::initClass();
2627 SoSFVec2s::initClass();
2628 SoSFVec2i32::initClass();
2629 SoSFVec2f::initClass();
2630 SoSFVec2d::initClass();
2631 SoSFVec3b::initClass();
2632 SoSFVec3s::initClass();
2633 SoSFVec3i32::initClass();
2634 SoSFVec3f::initClass();
2635 SoSFVec3d::initClass();
2636 SoSFVec4b::initClass();
2637 SoSFVec4ub::initClass();
2638 SoSFVec4s::initClass();
2639 SoSFVec4us::initClass();
2640 SoSFVec4i32::initClass();
2641 SoSFVec4ui32::initClass();
2642 SoSFVec4f::initClass();
2643 SoSFVec4d::initClass();
2644 SoSFMatrix::initClass();
2645 SoSFEnum::initClass();
2646 SoSFBitMask::initClass();
2647 SoSFImage::initClass();
2648 SoSFImage3::initClass();
2649 SoSFName::initClass();
2650 SoSFNode::initClass();
2651 SoSFPath::initClass();
2652 SoSFPlane::initClass();
2653 SoSFRotation::initClass();
2654 SoSFString::initClass();
2655 SoSFTime::initClass();
2656 SoSFTrigger::initClass();
2657
2658 SoMField::initClass();
2659 SoMFBool::initClass();
2660 SoMFColor::initClass();
2661 SoMFColorRGBA::initClass();
2662 SoMFDouble::initClass();
2663 SoMFEngine::initClass();
2664 SoMFEnum::initClass();
2665 SoMFBitMask::initClass();
2666 SoMFFloat::initClass();
2667 SoMFInt32::initClass();
2668 SoMFMatrix::initClass();
2669 SoMFName::initClass();
2670 SoMFNode::initClass();
2671 SoMFPath::initClass();
2672 SoMFPlane::initClass();
2673 SoMFRotation::initClass();
2674 SoMFShort::initClass();
2675 SoMFString::initClass();
2676 SoMFTime::initClass();
2677 SoMFUInt32::initClass();
2678 SoMFUShort::initClass();
2679 SoMFVec2b::initClass();
2680 SoMFVec2s::initClass();
2681 SoMFVec2i32::initClass();
2682 SoMFVec2f::initClass();
2683 SoMFVec2d::initClass();
2684 SoMFVec3b::initClass();
2685 SoMFVec3s::initClass();
2686 SoMFVec3i32::initClass();
2687 SoMFVec3f::initClass();
2688 SoMFVec3d::initClass();
2689 SoMFVec4b::initClass();
2690 SoMFVec4ub::initClass();
2691 SoMFVec4s::initClass();
2692 SoMFVec4us::initClass();
2693 SoMFVec4i32::initClass();
2694 SoMFVec4ui32::initClass();
2695 SoMFVec4f::initClass();
2696 SoMFVec4d::initClass();
2697
2698 // Create these obsoleted types for backwards compatibility. They
2699 // are typedef'ed to the types which obsoleted them, but this is
2700 // needed so it will also be possible to use SoType::fromName() with
2701 // the old names and create instances in that manner.
2702 //
2703 // FIXME: SoType::fromName("oldname") == SoType::fromName("newname")
2704 // will fail, but this can be solved with a hack in
2705 // SoType::operator==(). Do we _want_ to implement this hack,
2706 // though? It'd be ugly as hell. 19991109 mortene.
2707 // Does it need to be so ugly? == could compare createInstance
2708 // pointers if both have is set? But would it be correct, and would
2709 // any code depend on or benefit from such behaviour? 20070518 larsa
2710 SoType::createType(SoSField::getClassTypeId(), "SFLong",
2711 &SoSFInt32::createInstance);
2712 SoType::createType(SoSField::getClassTypeId(), "SFULong",
2713 &SoSFUInt32::createInstance);
2714 SoType::createType(SoMField::getClassTypeId(), "MFLong",
2715 &SoMFInt32::createInstance);
2716 SoType::createType(SoMField::getClassTypeId(), "MFULong",
2717 &SoMFUInt32::createInstance);
2718 }
2719
2720 #undef FLAG_ALIVE_PATTERN
2721 #undef SOFIELD_RECLOCK
2722 #undef SOFIELD_RECUNLOCK
2723