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