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 SoEngineOutput SoEngine.h Inventor/engines/SoEngine.h
35 \brief The SoEngineOutput class is the output slots in SoEngine instances.
36
37 \ingroup engines
38
39 SoEngineOutput has methods for convenient handling of the
40 connections made from SoEngine objects to SoField objects.
41
42 \sa SoEngine, SoField
43 */
44
45 #include <Inventor/engines/SoEngineOutput.h>
46
47 #include "SbBasicP.h"
48
49 #include <Inventor/engines/SoEngine.h>
50 #include <Inventor/engines/SoNodeEngine.h>
51 #include <Inventor/engines/SoOutputData.h>
52 #include <Inventor/engines/SoFieldConverter.h>
53 #include <Inventor/fields/SoField.h>
54 #include <Inventor/misc/SoProtoInstance.h>
55
56 #if COIN_DEBUG
57 #include <Inventor/errors/SoDebugError.h>
58 #endif // COIN_DEBUG
59
60 /*!
61 Constructor. The SoEngineOutput will initially not be contained
62 within an SoEngine nor will it have any slave fields attached.
63
64 \sa setContainer()
65 */
SoEngineOutput(void)66 SoEngineOutput::SoEngineOutput(void)
67 {
68 this->enabled = TRUE;
69 this->container = NULL;
70 }
71
72 /*!
73 Destructor.
74 */
~SoEngineOutput()75 SoEngineOutput::~SoEngineOutput()
76 {
77 #if COIN_DEBUG && 0 // debug
78 SoDebugError::postInfo("SoEngineOutput::~SoEngineOutput", "start %p", this);
79 #endif // debug
80
81 // Avoids evaluation from the fields in SoField::disconnect() (which
82 // would again lead to problems with the pure virtual
83 // SoEngine::evaluate() function during destruction of our
84 // container).
85 this->enabled = FALSE;
86
87 // Disconnect all fields.
88 SoFieldList fl;
89 int nr = this->getForwardConnections(fl);
90 for (int i=0; i < nr; i++) fl[i]->disconnect(this);
91
92 #if COIN_DEBUG && 0 // debug
93 SoDebugError::postInfo("SoEngineOutput::~SoEngineOutput", "done %p", this);
94 #endif // debug
95 }
96
97 /*!
98 Returns the type of the engine output.
99 */
100 SoType
getConnectionType(void) const101 SoEngineOutput::getConnectionType(void) const
102 {
103 assert(this->container != NULL);
104 const SoEngineOutputData * outputs =
105 this->isNodeEngineOutput() ?
106 this->getNodeContainer()->getOutputData() :
107 this->getContainer()->getOutputData();
108 assert(outputs);
109 int idx = outputs->getIndex(this->container, this);
110 assert(idx >= 0 && "an engine should have at least one output");
111 return outputs->getType(idx);
112 }
113
114 /*!
115 Adds all slave fields connected to this output to \a fl. Returns
116 the number of slaves.
117
118 \sa addConnection()
119 \sa removeConnection()
120 */
121 int
getForwardConnections(SoFieldList & fl) const122 SoEngineOutput::getForwardConnections(SoFieldList & fl) const
123 {
124 int numc = 0;
125 for (int i = 0; i < this->slaves.getLength(); i++) {
126 SoField * field = this->slaves[i];
127 SoFieldContainer * fc = field->getContainer();
128 // test for and skip field converters. Get the real field this
129 // output is connected to.
130 if (fc && fc->isOfType(SoFieldConverter::getClassTypeId())) {
131 SoFieldConverter * converter = coin_assert_cast<SoFieldConverter *>(fc);
132 numc += converter->getForwardConnections(fl);
133 }
134 else {
135 fl.append(field);
136 numc++;
137 }
138 }
139 return numc;
140 }
141
142 /*!
143 Sets the enabled flag. If output is disabled, the fields connected
144 to this output will not be changed when the engine is evaluated.
145
146 \sa isEnabled().
147 */
148 void
enable(const SbBool flag)149 SoEngineOutput::enable(const SbBool flag)
150 {
151 this->enabled = flag;
152 if (flag) this->touchSlaves(NULL, FALSE);
153 }
154
155 /*!
156 Returns status of the enabled flag.
157
158 \sa enable().
159 */
160 SbBool
isEnabled(void) const161 SoEngineOutput::isEnabled(void) const
162 {
163 return this->enabled;
164 }
165
166 /*!
167 Returns the engine containing this output. If the engine
168 containing this output is a NodeEngine, this method returns NULL.
169
170 \sa setContainer(), getNodeContainer()
171 */
172 SoEngine *
getContainer(void) const173 SoEngineOutput::getContainer(void) const
174 {
175 if (this->isNodeEngineOutput()) {
176 #if COIN_DEBUG
177 SoDebugError::postWarning("SoEngineOutput::getContainer",
178 "Container is not an Engine");
179 #endif // COIN_DEBUG
180 return NULL;
181 }
182 return this->container;
183 }
184
185 /*!
186
187 Returns the node engine containing this output. If the engine
188 containing this output is not a NodeEgine, this method returns NULL.
189
190 \COIN_FUNCTION_EXTENSION
191
192 \sa setNodeContainer(), getContainer()
193 \since Coin 2.0
194 */
195
196 SoNodeEngine *
getNodeContainer(void) const197 SoEngineOutput::getNodeContainer(void) const
198 {
199 if (!this->isNodeEngineOutput()) {
200 #if COIN_DEBUG
201 SoDebugError::postWarning("SoEngineOutput::getContainer",
202 "Container is not a NodeEngine");
203 #endif // COIN_DEBUG
204 return NULL;
205 }
206 return coin_assert_cast<SoNodeEngine*>(this->container);
207 }
208
209 /*!
210 Returns \e TRUE if the container is a NodeEngine.
211
212 \COIN_FUNCTION_EXTENSION
213
214 \sa getNodeContainer(), getContainer()
215 \since Coin 2.0
216 */
217 SbBool
isNodeEngineOutput(void) const218 SoEngineOutput::isNodeEngineOutput(void) const
219 {
220 assert(this->container);
221 return this->container->getTypeId().isDerivedFrom(SoNodeEngine::getClassTypeId());
222 }
223
224 /*!
225 Sets the engine containing this output.
226
227 \sa getContainer()
228 */
229 void
setContainer(SoEngine * engine)230 SoEngineOutput::setContainer(SoEngine * engine)
231 {
232 this->container = engine;
233 }
234
235
236 /*!
237 Sets the NodeEngine containing this output.
238
239 \COIN_FUNCTION_EXTENSION
240
241 \sa getNodeContainer()
242 \since Coin 2.0
243 */
244
245 void
setNodeContainer(SoNodeEngine * nodeengine)246 SoEngineOutput::setNodeContainer(SoNodeEngine * nodeengine)
247 {
248 // FIXME: hack cast to SoEngine. The type of the container member
249 // needs to be SoFieldContainer, not SoEngine.
250 this->container = reinterpret_cast<SoEngine*> (nodeengine);
251 }
252
253 /*!
254 Adds \a f to the list of connections from this output.
255
256 Adds 1 to the reference count of the container engine.
257
258 \sa removeConnection(), getForwardConnections()
259 */
260 void
addConnection(SoField * f)261 SoEngineOutput::addConnection(SoField * f)
262 {
263 #if COIN_DEBUG
264 if (this->slaves.find(f) != -1) {
265 SoDebugError::postWarning("SoEngineOutput::addConnection",
266 "connection from %p already made", f);
267 return;
268 }
269 #endif // COIN_DEBUG
270
271 this->slaves.append(f);
272
273 // An engine's reference count increases with the number of
274 // connections it has.
275
276 // SoProtoInstance has some special memory handling. Don't ref
277 // and unref if the connection is to an SoProtoInstance
278 SoFieldContainer * fc = f->getContainer();
279
280 if (fc && !fc->isOfType(SoProtoInstance::getClassTypeId()) &&
281 // don't reference if the slave and master are in the same container
282 // else the corresponding unref can never happen RHW
283 fc != this->getFieldContainer ()) {
284 this->getFieldContainer()->ref();
285 }
286
287 // Trigger re-evaluation of engine.
288 if (!this->isNodeEngineOutput()) {
289 this->getContainer()->setDirty();
290 }
291 }
292
293 /*!
294 Removes \a f from the list of connections from this output.
295
296 Subtracts 1 from the reference count of the container engine. If the
297 reference count reaches zero (which will happen if this was the last
298 connection and the application programmer did not explicitly \e ref
299 the engine), the container engine will be deallocated.
300
301 \sa addConnection(), getForwardConnections()
302 */
303 void
removeConnection(SoField * f)304 SoEngineOutput::removeConnection(SoField * f)
305 {
306 int i = this->slaves.find(f);
307 #if COIN_DEBUG
308 if (i == -1) {
309 SoDebugError::postWarning("SoEngineOutput::removeConnection",
310 "no connection from %p present", f);
311 return;
312 }
313 #endif // COIN_DEBUG
314 this->slaves.remove(i);
315
316 // SoProtoInstance has some special memory handling. Don't ref
317 // and unref if the connection is to an SoProtoInstance
318 SoFieldContainer * fc = f->getContainer();
319
320 if (fc && !fc->isOfType(SoProtoInstance::getClassTypeId()) &&
321 // we don't want to unref when connected to self RHW
322 fc != this->getFieldContainer ()) {
323 this->getFieldContainer()->unref();
324 }
325 }
326
327 /*!
328 Returns the number of fields in the list of connections.
329
330 \sa operator[], addConnection()
331 */
332 int
getNumConnections(void) const333 SoEngineOutput::getNumConnections(void) const
334 {
335 return this->slaves.getLength();
336 }
337
338 /*!
339 Returns the field at index \a i in the list of connections.
340
341 Please note that this might not be the same field as the one the
342 output was originally connected to, since a field converted might
343 have been inserted to converted the output to match the field type.
344
345 \sa getNumConnections()
346 */
347 SoField *
operator [](int i) const348 SoEngineOutput::operator[](int i) const
349 {
350 return this->slaves[i];
351 }
352
353 /*!
354 Disables notifications on fields connected to this output. This is
355 done before the engine is evaulated, since the fields we are going
356 to write into have already been notified.
357
358 \sa doneWriting()
359 */
360 void
prepareToWrite(void) const361 SoEngineOutput::prepareToWrite(void) const
362 {
363 SoEngineOutput * that = const_cast<SoEngineOutput *>(this);
364 that->fieldnotiflist.truncate(0);
365
366 int n = this->slaves.getLength();
367 for (int i = 0; i < n; i++) {
368 SoField * f = this->slaves[i];
369 that->fieldnotiflist.append(f->isNotifyEnabled());
370 f->enableNotify(FALSE);
371 }
372 }
373
374 /*!
375 Restores the notification flags on fields connected to this output
376 after evaluating.
377
378 \sa prepareToWrite()
379 */
380 void
doneWriting(void) const381 SoEngineOutput::doneWriting(void) const
382 {
383 // We should have the exact same set of slave fields now as on entry
384 // to prepareToWrite(), as the field writing is supposed be a
385 // "closed" operation. (All notifications on the fields are
386 // disabled, so no application code should be able to intervene in
387 // SoEngine evaluation.)
388
389 int n = this->slaves.getLength();
390 assert(n == this->fieldnotiflist.getLength());
391
392 const SbBool * notifs = this->fieldnotiflist.getArrayPtr();
393
394 for (int i = 0; i < n; i++) {
395 this->slaves[i]->enableNotify(notifs[i]);
396 }
397 }
398
399 /*!
400 Notify the fields attached to this engine output that the output
401 value has changed.
402
403 If \a donotify is \c TRUE, propagate the notification to the
404 fields. Otherwise just mark the fields "dirty" for re-evalution.
405
406 Note that this method is not part of the original Open Inventor API.
407 */
408 void
touchSlaves(SoNotList * nl,SbBool donotify)409 SoEngineOutput::touchSlaves(SoNotList * nl, SbBool donotify)
410 {
411 if (this->isEnabled()) {
412 // use a copy of the notification list so that the connections are
413 // not added to the original list
414 SoNotList listcopy;
415 if (nl && donotify) listcopy = *nl;
416 int numconnections = this->getNumConnections();
417 for (int j = 0; j < numconnections; j++) {
418 SoField * field = (*this)[j];
419 if (donotify) {
420 field->notify(&listcopy);
421 if (j < numconnections-1) {
422 listcopy = *nl;
423 }
424 }
425 else field->setDirty(TRUE);
426 }
427 }
428 }
429
430 /*!
431 Convenience method that returns a field container. This method can
432 be used both for NodeEngine and Engine outputs.
433
434 \COIN_FUNCTION_EXTENSION
435
436 \since Coin 2.0
437 */
438 SoFieldContainer *
getFieldContainer(void)439 SoEngineOutput::getFieldContainer(void)
440 {
441 return coin_assert_cast<SoFieldContainer *>(this->container);
442 }
443