1//$ TEMPLATE MFNodeEnginePath(_TYPENAME_, _Typename_, _typename_) 2 3/*! 4 \class SoMF_Typename_ SoMF_Typename_.h Inventor/fields/SoMF_Typename_.h 5 \brief The SoMF_Typename_ class is a container for _typename_s. 6 \ingroup fields 7 8 This field container stores an array of pointers to _typename_s. It takes 9 care of the necessary functionality for handling copy, import and 10 export operations. 11 12 Note that _typename_ pointers stored in field instances of this type may 13 be \c NULL pointers. 14 15 \sa So_Typename_, SoSF_Typename_ 16 17*/ 18 19// Type-specific define to be able to do #ifdef tests on type. (Note: 20// used to check the header file wrapper define, but that doesn't work 21// with --enable-compact build.) 22#define COIN_INTERNAL_SOMF_TYPENAME_ 23 24#include <Inventor/fields/SoMF_Typename_.h> 25#include <Inventor/fields/SoSubFieldP.h> 26#include <Inventor/fields/SoSF_Typename_.h> 27#include <Inventor/SoOutput.h> 28#include <Inventor/actions/SoWriteAction.h> 29#include <Inventor/SoPath.h> 30#include <Inventor/engines/SoEngine.h> 31#include <Inventor/nodes/SoNode.h> 32#if COIN_DEBUG 33#include <Inventor/errors/SoDebugError.h> 34#endif // COIN_DEBUG 35 36// These are the macros from SO_MFIELD_SOURCE_MALLOC we're 37// using. What's missing is the SO_MFIELD_VALUE_SOURCE macro, which we 38// need to implement "by hand" so reference counting and auditing 39// comes out correctly. 40SO_MFIELD_REQUIRED_SOURCE(SoMF_Typename_); 41SO_MFIELD_CONSTRUCTOR_SOURCE(SoMF_Typename_); 42SO_MFIELD_MALLOC_SOURCE(SoMF_Typename_, So_Typename_ *); 43// Note that we're using the MALLOC versions (which just does 44// bit-copying) of the macros, and not the the ALLOC versions (which 45// allocates with "new", so constructors are run). The reason for this 46// is that it's node/engine/path *pointers* that are simply bit-wise 47// copied. 48 49 50// Override from parent class. 51void 52SoMF_Typename_::initClass(void) 53{ 54 SO_MFIELD_INTERNAL_INIT_CLASS(SoMF_Typename_); 55} 56 57 58// No need to document readValue() and writeValue() here, as the 59// necessary information is provided by the documentation of the 60// parent classes. 61#ifndef DOXYGEN_SKIP_THIS 62 63//// From the SO_MFIELD_VALUE_SOURCE macro, start. /////////////////////////// 64 65// We can't use the macro invocation, as we need to take care of doing 66// ref() and unref() on the _typename_s in the array. 67 68int 69SoMF_Typename_::fieldSizeof(void) const 70{ 71 return sizeof(So_Typename_ *); 72} 73 74void * 75SoMF_Typename_::valuesPtr(void) 76{ 77 return (void *)this->values; 78} 79 80void 81SoMF_Typename_::setValuesPtr(void * ptr) 82{ 83 // We don't get any ref()'ing done here, or any notification 84 // mechanisms set up -- so this function should _only_ be used for 85 // initial setup of array memory. In Coin, it's only used from 86 // SoMField::allocValues(). 87 this->values = (So_Typename_ **)ptr; 88} 89 90int 91SoMF_Typename_::find(So_Typename_ * value, SbBool addifnotfound) 92{ 93 for (int i=0; i < this->num; i++) if ((*this)[i] == value) return i; 94 95 if (addifnotfound) this->set1Value(this->num, value); 96 return -1; 97} 98 99void 100SoMF_Typename_::setValues(const int start, const int num, const So_Typename_ ** newvals) 101{ 102 // Disable temporarily, so we under any circumstances will not send 103 // more than one notification about the changes. 104 SbBool notificstate = this->enableNotify(FALSE); 105 // Important note: the notification state is reset at the end, so 106 // this function should *not* have multiple return-points. 107 108 // ref() new _typename_s before unref()-ing old ones, in case there are 109 // common _typename_s (we don't want any premature destruction to happen). 110 { for (int i=0; i < num; i++) if (newvals[i]) newvals[i]->ref(); } 111 112 // We favor simplicity of code over performance here. 113 { for (int i=0; i < num; i++) 114 this->set1Value(start+i, (So_Typename_ *)newvals[i]); } 115 116 // unref() to match the initial ref(). 117 { for (int i=0; i < num; i++) if (newvals[i]) newvals[i]->unref(); } 118 119 // Finally, send notification. 120 (void)this->enableNotify(notificstate); 121 if (notificstate) this->valueChanged(); 122} 123 124void 125SoMF_Typename_::set1Value(const int idx, So_Typename_ * newval) 126{ 127 // Disable temporarily, so we under no circumstances will send more 128 // than one notification about the change. 129 SbBool notificstate = this->enableNotify(FALSE); 130 // Important note: the notification state is reset at the end, so 131 // this function should *not* have multiple return-points. 132 133 // Don't use getNum(), getValues() or operator[] to find old values, 134 // since this might trigger a recursive evaluation call if the field 135 // is connected. 136 137 // Expand array if necessary. 138 if (idx >= this->num) { 139#ifdef COIN_INTERNAL_SOMFPATH 140 for (int i = this->num; i <= idx; i++) this->pathheads.append(NULL); 141#endif // COIN_INTERNAL_SOMFPATH 142 this->setNum(idx + 1); 143 } 144 145 So_Typename_ * oldptr = this->values[idx]; 146 if (oldptr != newval) { 147 if (oldptr) { 148#ifdef COIN_INTERNAL_SOMFPATH 149 SoNode * h = oldptr->getHead(); 150 // The path should be audited by us at all times. So don't use 151 // SoMFPath to wrap SoTempPath or SoLightPath, for instance. 152 assert(h==this->pathheads[idx] && 153 "Path head changed without notification!"); 154 if (h) { 155 h->removeAuditor(this, SoNotRec::FIELD); 156 h->unref(); 157 } 158#endif // COIN_INTERNAL_SOMFPATH 159 oldptr->removeAuditor(this, SoNotRec::FIELD); 160 oldptr->unref(); 161 } 162 163 if (newval) { 164 newval->addAuditor(this, SoNotRec::FIELD); 165 newval->ref(); 166#ifdef COIN_INTERNAL_SOMFPATH 167 SoNode * h = newval->getHead(); 168 if (h) { 169 h->addAuditor(this, SoNotRec::FIELD); 170 h->ref(); 171 } 172#endif // COIN_INTERNAL_SOMFPATH 173 } 174 175 this->values[idx] = newval; 176#ifdef COIN_INTERNAL_SOMFPATH 177 this->pathheads[idx] = newval ? newval->getHead() : NULL; 178#endif // COIN_INTERNAL_SOMFPATH 179 } 180 181 // Finally, send notification. 182 (void)this->enableNotify(notificstate); 183 if (notificstate) this->valueChanged(); 184} 185 186void 187SoMF_Typename_::setValue(So_Typename_ * value) 188{ 189 this->deleteAllValues(); 190 this->set1Value(0, value); 191} 192 193SbBool 194SoMF_Typename_::operator==(const SoMF_Typename_ & field) const 195{ 196 if (this == &field) return TRUE; 197 if (this->getNum() != field.getNum()) return FALSE; 198 199 const So_Typename_ ** const lhs = this->getValues(0); 200 const So_Typename_ ** const rhs = field.getValues(0); 201 for (int i = 0; i < num; i++) if (lhs[i] != rhs[i]) return FALSE; 202 return TRUE; 203} 204 205/*! 206 \copydoc SoMFFloat::deleteAllValues() 207*/ 208void 209SoMF_Typename_::deleteAllValues(void) 210{ 211 // Don't use getNum(), but use this->num directly, since getNum() 212 // might trigger a recursive evaluation call if the field 213 // is connected. 214 215 if (this->num) this->deleteValues(0); 216} 217 218// Overridden to handle unref() and removeAuditor(). 219void 220SoMF_Typename_::deleteValues(int start, int num) 221{ 222 // Note: this function overrides the one in SoMField, so if you do 223 // any changes here, take a look at that method aswell. 224 225 if (num == -1) num = this->num - start; 226 for (int i=start; i < start+num; i++) { 227 So_Typename_ * n = this->values[i]; 228 if (n) { 229 n->removeAuditor(this, SoNotRec::FIELD); 230 n->unref(); 231 } 232#ifdef COIN_INTERNAL_SOMFPATH 233 SoNode * h = this->pathheads[start]; 234 this->pathheads.remove(start); 235 if (h) { 236 h->removeAuditor(this, SoNotRec::FIELD); 237 h->unref(); 238 } 239#endif // COIN_INTERNAL_SOMFPATH 240 } 241 242 inherited::deleteValues(start, num); 243} 244 245// Overridden to insert NULL pointers in new array slots. 246void 247SoMF_Typename_::insertSpace(int start, int num) 248{ 249 // Disable temporarily so we don't send notification prematurely 250 // from inherited::insertSpace(). 251 SbBool notificstate = this->enableNotify(FALSE); 252 // Important note: the notification state is reset at the end, so 253 // this function should *not* have multiple return-points. 254 255 inherited::insertSpace(start, num); 256 for (int i=start; i < start+num; i++) { 257#ifdef COIN_INTERNAL_SOMFPATH 258 this->pathheads.insert(NULL, start); 259#endif // COIN_INTERNAL_SOMFPATH 260 this->values[i] = NULL; 261 } 262 263 // Initialization done, now send notification. 264 (void)this->enableNotify(notificstate); 265 if (notificstate) this->valueChanged(); 266} 267 268/*! 269 \copydoc SoMFFloat::copyValue() 270*/ 271void 272SoMF_Typename_::copyValue(int to, int from) 273{ 274 this->values[to] = this->values[from]; 275} 276 277//// From the SO_MFIELD_VALUE_SOURCE macro, end. ///////////////////////////// 278 279 280// Import a single _typename_. 281SbBool 282SoMF_Typename_::read1Value(SoInput * in, int index) 283{ 284 SoSF_Typename_ sf_typename_; 285 SbBool result = sf_typename_.readValue(in); 286 if (result) this->set1Value(index, sf_typename_.getValue()); 287 return result; 288} 289 290// Export a single _typename_. 291void 292SoMF_Typename_::write1Value(SoOutput * out, int idx) const 293{ 294 // NB: This code is common for SoMFNode, SoMFPath and SoMFEngine. 295 // That's why we check for the base type before writing. 296 297 SoBase * base = (SoBase*) this->values[idx]; 298 if (base) { 299 if (base->isOfType(SoNode::getClassTypeId())) { 300 ((SoNode*)base)->writeInstance(out); 301 } 302 else if (base->isOfType(SoPath::getClassTypeId())) { 303 SoWriteAction wa(out); 304 wa.continueToApply((SoPath*)base); 305 } 306 else if (base->isOfType(SoEngine::getClassTypeId())) { 307 ((SoEngine*)base)->writeInstance(out); 308 } 309 } 310 else { 311 out->write("NULL"); 312 } 313} 314 315#endif // DOXYGEN_SKIP_THIS 316 317 318// Overridden from parent to propagate write reference counting to 319// _typename_. 320void 321SoMF_Typename_::countWriteRefs(SoOutput * out) const 322{ 323 inherited::countWriteRefs(out); 324 325 for (int i = 0; i < this->getNum(); i++) { 326 SoBase * base = this->values[i]; 327 if (base) { 328 // NB: This code is common for SoMFNode, SoMFPath and SoMFEngine. 329 // That's why we check the base type before writing/counting 330 331 if (base->isOfType(SoNode::getClassTypeId())) { 332 ((SoNode*)base)->writeInstance(out); 333 } 334 else if (base->isOfType(SoEngine::getClassTypeId())) { 335 ((SoEngine*)base)->addWriteReference(out); 336 } 337 else if (base->isOfType(SoPath::getClassTypeId())) { 338 SoWriteAction wa(out); 339 wa.continueToApply((SoPath*)base); 340 } 341 } 342 } 343} 344 345// Override from parent to update our _typename_ pointer 346// references. This is necessary so we do the Right Thing with regard 347// to the copyconnections flag. 348// 349// Note that we have to unplug auditing and the reference counter 350// addition we made during the copy process. 351// 352// For reference for future debugging sessions, copying of this field 353// goes like this: 354// 355// - copyFrom() is called (typically from SoFieldData::overlay()) 356// - copyFrom() calls operator=() 357// - operator=() calls setValues() 358// - we have a local copy (ie not from SoSubField.h) of setValues() 359// that sets up auditing and references the array items 360// 361// <mortene@sim.no> 362void 363SoMF_Typename_::fixCopy(SbBool copyconnections) 364{ 365 // Disable temporarily, so we under no circumstances will send more 366 // than one notification about the changes. 367 SbBool notificstate = this->enableNotify(FALSE); 368 // Important note: the notification state is reset at the end, so 369 // this function should *not* have multiple return-points. 370 371 for (int i=0; i < this->getNum(); i++) { 372 So_Typename_ * n = (*this)[i]; 373 if (n) { 374#if COIN_DEBUG 375 n->assertAlive(); 376#endif // COIN_DEBUG 377 // The set1Value() call below will automatically de-audit and 378 // un-ref the old pointer value node reference we have in the 379 // array, *before* re-inserting a copy. 380 381#if defined(COIN_INTERNAL_SOMFNODE) || defined(COIN_INTERNAL_SOMFENGINE) 382 SoFieldContainer * fc = SoFieldContainer::findCopy(n, copyconnections); 383#if COIN_DEBUG 384 if (fc) fc->assertAlive(); 385#endif // COIN_DEBUG 386 if (fc) this->set1Value(i, (So_Typename_ *)fc); 387#endif // COIN_INTERNAL_SOMFNODE || COIN_INTERNAL_SOMFENGINE 388 389#ifdef COIN_INTERNAL_SOMFPATH 390 this->set1Value(i, n->copy()); 391#endif // COIN_INTERNAL_SOMFPATH 392 } 393 } 394 395 // Finally, send notification. 396 (void)this->enableNotify(notificstate); 397 if (notificstate) this->valueChanged(); 398} 399 400// Override from SoField to check _typename_ pointer. 401SbBool 402SoMF_Typename_::referencesCopy(void) const 403{ 404 if (inherited::referencesCopy()) return TRUE; 405 406 for (int i=0; i < this->getNum(); i++) { 407 So_Typename_ * item = (*this)[i]; 408 if (item) { 409#if defined(COIN_INTERNAL_SOMFNODE) || defined(COIN_INTERNAL_SOMFENGINE) 410 if (SoFieldContainer::checkCopy((SoFieldContainer *)item)) return TRUE; 411#endif // COIN_INTERNAL_SOMFNODE || COIN_INTERNAL_SOMFENGINE 412#ifdef COIN_INTERNAL_SOMFPATH 413 if (item->getHead() && SoFieldContainer::checkCopy(item->getHead())) return TRUE; 414#endif // COIN_INTERNAL_SOMFPATH 415 } 416 } 417 418 return FALSE; 419} 420 421// Kill the type-specific define. 422#undef COIN_INTERNAL_SOMF_TYPENAME_ 423