1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36
37 #ifdef HAVE_NODEKITS
38
39 /*!
40 \class SoNodekitCatalog SoNodekitCatalog.h Inventor/nodekits/SoNodekitCatalog.h
41 \brief The SoNodekitCatalog class is a container for nodekit layouts.
42
43 \ingroup nodekits
44
45 Nodekits store all their hierarchical layout information and part
46 information in instances of this class.
47
48 \sa SoNodeKit, SoBaseKit
49 */
50
51 #include <Inventor/nodekits/SoBaseKit.h>
52
53 #include <cassert>
54 #include <cstdio> // fprintf()
55
56 #include <Inventor/lists/SoTypeList.h>
57 #if COIN_DEBUG
58 #include <Inventor/errors/SoDebugError.h>
59 #endif // COIN_DEBUG
60
61 #include "threads/threadsutilp.h"
62
63 // Private container class.
64 class CatalogItem {
65 public:
CatalogItem(void)66 CatalogItem(void) { }
~CatalogItem()67 ~CatalogItem() { }
68
69 SbName name, parentname, siblingname;
70 SoType type, defaulttype, containertype;
71 SbBool isdefaultnull, islist, ispublic;
72 SoTypeList itemtypeslist;
73 };
74
75 /*!
76 Initialization of static variables.
77 */
78 void
initClass(void)79 SoNodekitCatalog::initClass(void)
80 {
81 }
82
83 /*!
84 Constructor.
85 */
SoNodekitCatalog(void)86 SoNodekitCatalog::SoNodekitCatalog(void)
87 {
88 }
89
90 /*!
91 Destructor.
92 */
~SoNodekitCatalog()93 SoNodekitCatalog::~SoNodekitCatalog()
94 {
95 int i;
96 for (i=0; i < this->items.getLength(); i++)
97 delete this->items[i];
98 for (i=0; i < this->delayeditems.getLength(); i++)
99 delete this->delayeditems[i];
100 }
101
102 /*!
103 Returns total number of entries in the catalog.
104 */
105 int
getNumEntries(void) const106 SoNodekitCatalog::getNumEntries(void) const
107 {
108 assert(this->delayeditems.getLength() == 0);
109 return this->items.getLength();
110 }
111
112 /*!
113 Returns part number in catalog with \a name. If no part exists with
114 \a name, returns \c SO_CATALOG_NAME_NOT_FOUND.
115 */
116 int
getPartNumber(const SbName & name) const117 SoNodekitCatalog::getPartNumber(const SbName & name) const
118 {
119 assert(this->delayeditems.getLength() == 0);
120 return this->getPartNumber(this->items, name);
121 }
122
123 /*!
124 Given the \a part number, return name of that part.
125 */
126 const SbName &
getName(int part) const127 SoNodekitCatalog::getName(int part) const
128 {
129 assert( this->delayeditems.getLength() == 0 );
130 assert( part >= 0 && part < this->getNumEntries() &&
131 "invalid part" );
132
133 return this->items[part]->name;
134 }
135
136 /*!
137 Given the \a part number, return type.
138 */
139 SoType
getType(int part) const140 SoNodekitCatalog::getType(int part) const
141 {
142 assert(this->delayeditems.getLength() == 0);
143 assert( part >= 0 && part < this->getNumEntries() &&
144 "invalid part" );
145 return this->items[part]->type;
146 }
147
148 /*!
149 Given the part \a name, return type.
150 */
151 SoType
getType(const SbName & name) const152 SoNodekitCatalog::getType(const SbName & name) const
153 {
154 assert(this->delayeditems.getLength() == 0);
155 return this->getType( this->getPartNumber( name ) );
156 }
157
158 /*!
159 Given \a part number, return default type of part.
160 */
161 SoType
getDefaultType(int part) const162 SoNodekitCatalog::getDefaultType(int part) const
163 {
164 assert(this->delayeditems.getLength() == 0);
165 assert( part >= 0 && part < this->getNumEntries() &&
166 "invalid part" );
167
168 return this->items[part]->defaulttype;
169 }
170
171 /*!
172 Given part \a name, return default type of part.
173 */
174 SoType
getDefaultType(const SbName & name) const175 SoNodekitCatalog::getDefaultType(const SbName & name) const
176 {
177 assert( this->delayeditems.getLength() == 0 );
178 return this->getDefaultType( this->getPartNumber( name ) );
179 }
180
181 /*!
182 Returns \c TRUE if the \a part is empty by default, otherwise \c FALSE.
183 */
184 SbBool
isNullByDefault(int part) const185 SoNodekitCatalog::isNullByDefault(int part) const
186 {
187 assert( this->delayeditems.getLength() == 0 );
188 assert( part != SO_CATALOG_NAME_NOT_FOUND &&
189 "invalid part");
190
191 return this->items[part]->isdefaultnull;
192 }
193
194 // Seems like there's a Doxygen bug (version 1.2.1, at least) as it
195 // reports isNullByDefault(const SbName & name) as undocumented
196 // without the explicit \fn.
197
198 /*!
199 \fn SbBool SoNodekitCatalog::isNullByDefault(const SbName & name) const
200
201 Returns \c TRUE if part \a name is empty by default, otherwise \c
202 FALSE.
203 */
204 SbBool
isNullByDefault(const SbName & name) const205 SoNodekitCatalog::isNullByDefault(const SbName & name) const
206 {
207 assert(this->delayeditems.getLength() == 0);
208 return this->isNullByDefault( this->getPartNumber(name) );
209 }
210
211 /*!
212 Returns \c TRUE if the \a part is \e not a parent for any
213 other parts in the nodekit catalog.
214 */
215 SbBool
isLeaf(int part) const216 SoNodekitCatalog::isLeaf(int part) const
217 {
218 assert( this->delayeditems.getLength() == 0);
219 assert( part >= 0 && part < this->getNumEntries() &&
220 "invalid part" );
221
222 for (int i=0; i < this->items.getLength(); i++) {
223 if ((i != part) && (this->items[part]->name == this->items[i]->parentname))
224 return FALSE;
225 }
226 return TRUE;
227 }
228
229 /*!
230 Returns \c TRUE if the part \a name is \e not a parent for any
231 other parts in the nodekit catalog.
232 */
233 SbBool
isLeaf(const SbName & name) const234 SoNodekitCatalog::isLeaf(const SbName & name) const
235 {
236 assert(this->delayeditems.getLength() == 0);
237 return this->isLeaf( this->getPartNumber( name ));
238 }
239
240 /*!
241 Returns name of parent of \a part. If \a part doesn't have a parent,
242 the empty string is returned.
243 */
244 const SbName &
getParentName(int part) const245 SoNodekitCatalog::getParentName(int part) const
246 {
247 assert( this->delayeditems.getLength() == 0);
248 assert( part >= 0 && part < this->getNumEntries() &&
249 "invalid part" );
250 return this->items[part]->parentname;
251 }
252
253 /*!
254 Returns name of parent of the part. If \a name doesn't have a parent,
255 the empty string is returned.
256 */
257 const SbName &
getParentName(const SbName & name) const258 SoNodekitCatalog::getParentName(const SbName & name) const
259 {
260 assert(this->delayeditems.getLength() == 0);
261 return this->getParentName( this->getPartNumber(name) );
262 }
263
264 /*!
265 Returns part number of given part's parent. If \a part doesn't have a parent,
266 SO_CATALOG_NAME_NOT_FOUND is returned.
267 */
268 int
getParentPartNumber(int part) const269 SoNodekitCatalog::getParentPartNumber(int part) const
270 {
271 assert( this->delayeditems.getLength() == 0 );
272 assert( part >= 0 && part < this->getNumEntries() &&
273 "invalid part" );
274
275 return this->getPartNumber(this->items[part]->parentname);
276 }
277
278 /*!
279 Returns part number of given part's parent. If \a name doesn't have a parent,
280 SO_CATALOG_NAME_NOT_FOUND is returned.
281 */
282 int
getParentPartNumber(const SbName & name) const283 SoNodekitCatalog::getParentPartNumber(const SbName & name) const
284 {
285 assert(this->delayeditems.getLength() == 0);
286 return this->getParentPartNumber( this->getPartNumber(name) );
287 }
288
289 /*!
290 Returns name of right sibling of \a part. Returns the empty string if
291 \a part doesn't have a right sibling.
292 */
293 const SbName &
getRightSiblingName(int part) const294 SoNodekitCatalog::getRightSiblingName(int part) const
295 {
296 assert(this->delayeditems.getLength() == 0);
297 assert( part >= 0 && part < this->getNumEntries() &&
298 "invalid part" );
299
300 return this->items[part]->siblingname;
301 }
302
303
304 /*!
305 Returns name of sibling of the part. Returns the empty string if
306 \a name doesn't have a right sibling.
307 */
308 const SbName &
getRightSiblingName(const SbName & name) const309 SoNodekitCatalog::getRightSiblingName(const SbName & name) const
310 {
311 assert(this->delayeditems.getLength() == 0);
312 return this->getRightSiblingName( this->getPartNumber(name) );
313 }
314
315 /*!
316 Returns part number of given part's sibling. Returns
317 SO_CATALOG_NAME_NOT_FOUND if \a part doesn't have a right sibling.
318 */
319 int
getRightSiblingPartNumber(int part) const320 SoNodekitCatalog::getRightSiblingPartNumber(int part) const
321 {
322 assert(this->delayeditems.getLength() == 0);
323 assert( part >= 0 && part < this->getNumEntries() &&
324 "invalid part" );
325
326 return this->getPartNumber(this->items[part]->siblingname);
327 }
328
329 /*!
330 Returns part number of given part's right sibling. Returns
331 SO_CATALOG_NAME_NOT_FOUND if part doesn't have a right sibling.
332 */
333 int
getRightSiblingPartNumber(const SbName & name) const334 SoNodekitCatalog::getRightSiblingPartNumber(const SbName & name) const
335 {
336 assert(this->delayeditems.getLength() == 0);
337
338 return this->getRightSiblingPartNumber( this->getPartNumber(name) );
339 }
340
341 /*!
342 Returns \c TRUE if the given \a part is a list container.
343 */
344 SbBool
isList(int part) const345 SoNodekitCatalog::isList(int part) const
346 {
347 assert(this->delayeditems.getLength() == 0);
348 assert( part >= 0 && part < this->getNumEntries() &&
349 "invalid part" );
350
351 return this->items[part]->islist;
352 }
353
354 /*!
355 Returns \c TRUE if the given part is a list container.
356 */
357 SbBool
isList(const SbName & name) const358 SoNodekitCatalog::isList(const SbName & name) const
359 {
360 assert(this->delayeditems.getLength() == 0);
361 return this->isList( this->getPartNumber(name) );
362 }
363
364 /*!
365 Returns type of list container (SoGroup, SoSeparator, SoSwitch, etc)
366 which \a part is.
367 */
368 SoType
getListContainerType(int part) const369 SoNodekitCatalog::getListContainerType(int part) const
370 {
371 assert( this->delayeditems.getLength() == 0 );
372 assert( part >= 0 && part < this->getNumEntries() &&
373 "invalid part" );
374 assert( this->items[part]->islist &&
375 "not a list container" );
376
377 return this->items[part]->containertype;
378 }
379
380 /*!
381 Returns type of list container (SoGroup, SoSeparator, SoSwitch, etc)
382 which the named part is.
383 */
384 SoType
getListContainerType(const SbName & name) const385 SoNodekitCatalog::getListContainerType(const SbName & name) const
386 {
387 assert(this->delayeditems.getLength() == 0);
388 return this->getListContainerType( this->getPartNumber(name) );
389 }
390
391 /*!
392 Returns list of node types which are allowed to be children of the
393 list container \a part.
394 */
395 const SoTypeList &
getListItemTypes(int part) const396 SoNodekitCatalog::getListItemTypes(int part) const
397 {
398 assert( this->delayeditems.getLength() == 0);
399 assert( (part >= 0 && part < this->getNumEntries()) &&
400 "invalid part");
401 assert( (this->items[part]->islist) &&
402 "part is not a list container" );
403
404 return this->items[part]->itemtypeslist;
405 }
406
407 /*!
408 Returns list of node types which are allowed to be children of the
409 named list container part.
410 */
411 const SoTypeList &
getListItemTypes(const SbName & name) const412 SoNodekitCatalog::getListItemTypes(const SbName & name) const
413 {
414 assert(this->delayeditems.getLength() == 0);
415 return this->getListItemTypes( this->getPartNumber(name) );
416 }
417
418 /*!
419 Returns \c TRUE if \a part is visible and publicly available for
420 queries and modifications, \c FALSE if \a part is hidden.
421 */
422 SbBool
isPublic(int part) const423 SoNodekitCatalog::isPublic(int part) const
424 {
425 assert( this->delayeditems.getLength() == 0 );
426 assert( (part >= 0 && part < this->getNumEntries()) && "invalid part" );
427
428 return this->items[part]->ispublic;
429 }
430
431 /*!
432 Returns \c TRUE if the part is visible and publicly available for
433 queries and modifications, \c FALSE if it is hidden.
434 */
435 SbBool
isPublic(const SbName & name) const436 SoNodekitCatalog::isPublic(const SbName & name) const
437 {
438 assert(this->delayeditems.getLength() == 0);
439 return this->isPublic( this->getPartNumber(name) );
440 }
441
442 /*!
443 Return a clone of this catalog. \a type will be used to set the type
444 and defaulttype values of the toplevel \c this entry.
445 */
446 SoNodekitCatalog *
clone(SoType type) const447 SoNodekitCatalog::clone(SoType type) const
448 {
449 assert(this->delayeditems.getLength() == 0);
450
451 SoNodekitCatalog * newcat = new SoNodekitCatalog;
452 for (int i=0; i < this->items.getLength(); i++) {
453 CatalogItem * olditem = this->items[i];
454 CatalogItem * newitem = new CatalogItem(*olditem);
455 if (i == 0) {
456 newitem->type = type;
457 newitem->defaulttype = type;
458 }
459 // This is the only element in CatalogItem which can't be bitwise
460 // copied.
461 newitem->itemtypeslist = olditem->itemtypeslist;
462 newcat->items.append(newitem);
463 }
464 return newcat;
465 }
466
SoNodekitCatalogPropagateDefaultInit(SoNodekitCatalog * pthis)467 static void SoNodekitCatalogPropagateDefaultInit( SoNodekitCatalog * pthis )
468 {
469
470 #if COIN_DEBUG && 0
471 SoDebugError::postInfo("SoNodekitCatalogPropagateDefaultInit",
472 "sanitizing catalog" );
473 #endif
474
475 for( int i = pthis->getNumEntries()-1; i > 0; --i ){
476 if( !pthis->isNullByDefault( i ) ){
477 SbName parent = pthis->getParentName( i );
478 if( pthis->isNullByDefault( parent ) )
479 pthis->setNullByDefault( parent, FALSE );
480 }
481 }
482 }
483
484
485 /*!
486 Add a new entry to the catalog. Returns \c TRUE if add was ok.
487 */
488 SbBool
addEntry(const SbName & name,SoType type,SoType defaulttype,SbBool isdefaultnull,const SbName & parentname,const SbName & rightsiblingname,SbBool islist,SoType listcontainertype,SoType listitemtype,SbBool ispublic)489 SoNodekitCatalog::addEntry(const SbName & name, SoType type,
490 SoType defaulttype, SbBool isdefaultnull,
491 const SbName & parentname,
492 const SbName & rightsiblingname,
493 SbBool islist, SoType listcontainertype,
494 SoType listitemtype, SbBool ispublic)
495 {
496 // The elements of a nodekit catalog is conceptually ordered like a
497 // tree, but implementation-wise we stuff them inside a list. This
498 // will make it speedier to access the elements through preset part
499 // number indices.
500 //
501 // The list is in the same order which we would get by continually
502 // appending elements through a prefix traversal of the tree.
503
504 // Note: this is a fix to make it possible to compile the
505 // SO_KIT_ADD_CATALOG_ENTRY() etc macros under MS VisualC++ with ""
506 // entries where you can specify blank , , entries under gcc.
507 SbName parent = parentname;
508 SbName rightsibling = rightsiblingname;
509 if (parent[0] == '\"' && parent[1] == '\"') parent = "";
510 if (rightsibling[0] == '\"' && rightsibling[1] == '\"') rightsibling = "";
511
512
513 #if COIN_DEBUG && 0
514 SoDebugError::postInfo("SoNodekitCatalog::addEntry",
515 "new entry: ``%s''", name.getString());
516 #endif
517
518 CC_GLOBAL_LOCK;
519 if (!this->hasEntry(name)) {
520 assert((name != "") && "Empty name not allowed");
521 assert((this->getPartNumber( this->items, name ) == SO_CATALOG_NAME_NOT_FOUND ) && "partname already in use" );
522 assert(this->getPartNumber( this->delayeditems, name ) == SO_CATALOG_NAME_NOT_FOUND && "partname already in use" );
523 assert( (parent!="" || this->getNumEntries() == 0) && "need a parent name" );
524 assert( (type != SoType::badType()) && "bad type" );
525 assert( (defaulttype != SoType::badType()) && "bad default type" );
526
527 // NOTE: !(A ^ B) <=> !A v !B
528 assert( (!islist || (listcontainertype != SoType::badType())) &&
529 "bad list container type");
530 assert( (!islist || (listitemtype != SoType::badType())) &&
531 "bad list item type" );
532
533 CatalogItem * newitem = new CatalogItem;
534 newitem->name = name;
535 newitem->type = type;
536 newitem->defaulttype = defaulttype;
537 newitem->isdefaultnull = isdefaultnull;
538 newitem->parentname = parent;
539 newitem->siblingname = rightsibling;
540 newitem->islist = islist;
541 newitem->containertype = listcontainertype;
542 newitem->itemtypeslist.append(listitemtype);
543 newitem->ispublic = ispublic;
544
545 SbBool delay = FALSE;
546 if (rightsibling != "" &&
547 this->getPartNumber(this->items, rightsibling) == SO_CATALOG_NAME_NOT_FOUND) {
548 delay = TRUE;
549 }
550 else if (parent != "" &&
551 this->getPartNumber(this->items, parent) == SO_CATALOG_NAME_NOT_FOUND) {
552 delay = TRUE;
553 }
554
555 if (delay)
556 this->delayeditems.append(newitem);
557 else
558 this->reallyAddEntry(newitem);
559
560 // Move elements from list of delayed inserts to "real" catalog
561 // list, if possible.
562 for (int i = 0; i < this->delayeditems.getLength(); i++) {
563 const SbName & p = this->delayeditems[i]->parentname;
564 const SbName & r = this->delayeditems[i]->siblingname;
565
566 if (this->getPartNumber(this->items, p) != SO_CATALOG_NAME_NOT_FOUND &&
567 ((r == "") ||
568 (this->getPartNumber(this->items, r) != SO_CATALOG_NAME_NOT_FOUND))){
569
570 this->reallyAddEntry(this->delayeditems[i]);
571 this->delayeditems.remove(i);
572 i = -1; // restart scan
573
574 #if COIN_DEBUG && 0
575 SoDebugError::postInfo("SoNodekitCatalog::addEntry",
576 "fixed delayed item, %d item%s left",
577 this->delayeditems.getLength(),
578 this->delayeditems.getLength()==1 ? "" : "s");
579 #endif
580 }
581 }
582
583 if( this->delayeditems.getLength() == 0 )
584 SoNodekitCatalogPropagateDefaultInit( this );
585 }
586 CC_GLOBAL_UNLOCK;
587 return TRUE;
588 }
589
590 // Add the item at the correct position in the entry list, where the
591 // arguemtn "newitem" is guaranteed to have both parent and rigt
592 // sibling (if any) present in the catalog.
593 SbBool
reallyAddEntry(CatalogItem * newitem)594 SoNodekitCatalog::reallyAddEntry(CatalogItem * newitem)
595 {
596 const int n = this->items.getLength();
597
598 if (n == 0) {
599 this->items.append(newitem);
600 return TRUE;
601 }
602
603 int position = -1;
604 for (int i = 0; i < n; i++) {
605 if ((this->items[i]->parentname == newitem->parentname) &&
606 (this->items[i]->siblingname == newitem->siblingname)) {
607 // this might happen when extending the catalog of another nodekit
608 this->items[i]->siblingname = newitem->name;
609 position = i+1;
610 break;
611 }
612 }
613 if (position < 0) {
614 position = 0;
615 while (position < n &&
616 (this->items[position]->name != newitem->siblingname ||
617 this->items[position]->parentname != newitem->parentname)) position++;
618 if (position == n) {
619 // parent and sibling not found, insert item after the parent
620 position = this->getPartNumber(this->items, newitem->parentname) + 1;
621 }
622 }
623 if (position == this->items.getLength())
624 this->items.append(newitem);
625 else
626 this->items.insert(newitem, position);
627 return TRUE;
628 }
629
630 /*!
631 Add another allowable type for the given \a part. \a part must of course
632 be a list container item.
633 */
634 void
addListItemType(int part,SoType type)635 SoNodekitCatalog::addListItemType(int part, SoType type)
636 {
637 assert(this->delayeditems.getLength() == 0);
638 this->addListItemType(this->items, part, type);
639 }
640
641 /*!
642 Add another allowable type for the \a name part. The part must of course
643 be a list container.
644 */
645 void
addListItemType(const SbName & name,SoType type)646 SoNodekitCatalog::addListItemType(const SbName & name, SoType type)
647 {
648 CC_GLOBAL_LOCK;
649 if (!this->hasListItemType(name, type)) {
650 // FIXME: If a part name is invalid, this procedure bails out
651 // elsewhere on an assert. The check and debug comment should be
652 // superflous? If it isn't, it should be possible to find a way to
653 // write this that expresses the code intentions better.
654 // 20021029 rolvs
655
656 if (!this->addListItemType(this->items, name, type) &&
657 !this->addListItemType(this->delayeditems, name, type)) {
658 #if COIN_DEBUG
659 SoDebugError::post("SoNodekitCatalog::addListItemType",
660 "invalid part name, \"%s\"", name.getString());
661 #endif
662 }
663 }
664 CC_GLOBAL_UNLOCK;
665 }
666
667 /*!
668 Set the type and default type of a part to be subtypes of the old
669 types. Useful for "narrowing" the specification of a nodekit which
670 inherits the catalog of a more generic nodekit superclass.
671 */
672 void
narrowTypes(const SbName & name,SoType newtype,SoType newdefaulttype)673 SoNodekitCatalog::narrowTypes(const SbName & name,
674 SoType newtype, SoType newdefaulttype)
675 {
676 CC_GLOBAL_LOCK;
677
678 assert(this->delayeditems.getLength() == 0);
679
680 int part = this->getPartNumber(name);
681
682 assert( part != SO_CATALOG_NAME_NOT_FOUND &&
683 "invalid part name" );
684 assert( newtype.isDerivedFrom( this->items[part]->type ) &&
685 "new type should be derived of old type" );
686 assert( newdefaulttype.isDerivedFrom( this->items[part]->type ) &&
687 "new type should be derived of old type" );
688
689 this->items[part]->type = newtype;
690 this->items[part]->defaulttype = newdefaulttype;
691
692 CC_GLOBAL_UNLOCK;
693 }
694
695 /*!
696 Change whether or not the part with the given \a name is created by
697 default.
698 */
699 void
setNullByDefault(const SbName & name,SbBool nullbydefault)700 SoNodekitCatalog::setNullByDefault(const SbName & name, SbBool nullbydefault)
701 {
702 assert(this->delayeditems.getLength() == 0);
703
704 int part = this->getPartNumber(name);
705 assert( part != SO_CATALOG_NAME_NOT_FOUND &&
706 "invalid part name" );
707
708 this->items[part]->isdefaultnull = nullbydefault;
709 }
710
711 /*!
712 Recursively search \a part number in catalog for the \a name part.
713
714 The \a checked SoTypeList is just used as a placeholder to remember which
715 nodekit class catalogs have already been scanned (or are being scanned)
716 during the recursion. You should normally just pass in an empty list.
717 */
718 SbBool
recursiveSearch(int part,const SbName & name,SoTypeList * checked) const719 SoNodekitCatalog::recursiveSearch(int part, const SbName & name,
720 SoTypeList * checked) const
721 {
722 assert( this->delayeditems.getLength() == 0 );
723 assert( part >= 0 && part < this->getNumEntries() &&
724 "part index out of bounds");
725
726 if ((part == 0) && (checked->find(this->getType(0)) == -1))
727 checked->append(this->getType(0));
728
729 int start = (part == 0) ? 1 : part;
730 int end = (part == 0) ? this->getNumEntries()-1 : part;
731
732 for (int i = start; i <= end; i++) {
733 if (name == this->getName(i)) return TRUE;
734
735 SoType parttype = this->getType(i);
736 if (parttype.isDerivedFrom(SoBaseKit::getClassTypeId())) {
737 if (checked->find(parttype) == -1) {
738 checked->append(parttype);
739 SoBaseKit * kit = (SoBaseKit *)parttype.createInstance();
740 kit->ref();
741 const SoNodekitCatalog * cat = kit->getNodekitCatalog();
742 SbBool result = cat->recursiveSearch(0, name, checked);
743 kit->unref();
744 if (result) return TRUE;
745 }
746 }
747 }
748
749 return FALSE;
750 }
751
752 /*!
753 Lists all catalog parts, which is useful for debugging.
754 */
755 void
printCheck(void) const756 SoNodekitCatalog::printCheck(void) const
757 {
758 int nritems = this->getNumEntries();
759 fprintf(stdout, "catalog printout: number of entries = %d\n", nritems);
760 for (int i=0; i < nritems; i++) {
761 CatalogItem * item = this->items[i];
762
763 fprintf(stdout,
764 "#%d\n"
765 " name = %s, type = %s, defaultType = %s\n"
766 " nullByDefault = %d\n"
767 " parentName = %s\n"
768 " sibling = %s, listPart = %d\n",
769 i, item->name.getString(),
770 item->type == SoType::badType() ? "*bad*" : item->type.getName().getString(),
771 item->defaulttype == SoType::badType() ? "*bad*" : item->defaulttype.getName().getString(),
772 item->isdefaultnull, item->parentname.getString(),
773 item->siblingname.getString(), item->islist);
774
775 if (item->islist) {
776 fprintf(stdout, "listItemTypes =");
777 for (int j=0; j < item->itemtypeslist.getLength(); j++) {
778 fprintf(stdout, " %s", item->itemtypeslist[j].getName().getString());
779 }
780 fprintf(stdout, "\n");
781 }
782
783 fprintf(stdout, " publicPart = %d\n", item->ispublic);
784 }
785 }
786
787 // Overloaded to work with both delayed and "real" list of entries.
788 int
getPartNumber(const SbList<class CatalogItem * > & l,const SbName & name) const789 SoNodekitCatalog::getPartNumber(const SbList<class CatalogItem *> & l,
790 const SbName & name) const
791 {
792 int nritems = l.getLength();
793 for (int i= 0; i < nritems; i++) {
794 if (name == l[i]->name) return i;
795 }
796 return SO_CATALOG_NAME_NOT_FOUND;
797 }
798
799 // Overloaded to work with both delayed and "real" list of entries.
800 void
addListItemType(const SbList<class CatalogItem * > & l,int part,SoType type)801 SoNodekitCatalog::addListItemType(const SbList<class CatalogItem *> & l,
802 int part, SoType type)
803 {
804 assert( part >= 0 && part < l.getLength() &&
805 "invalid part number" );
806 assert( type != SoType::badType() &&
807 "dont add SoType::badType(), you stupid!");
808 assert( l[part]->islist &&
809 "type must be a list-item type" );
810 assert( l[part]->itemtypeslist.find( type ) == -1 &&
811 "trying to add item that allready exists" );
812
813 l[part]->itemtypeslist.append(type);
814 }
815
816 // Overloaded to work with both delayed and "real" list of entries.
817 SbBool
addListItemType(const SbList<class CatalogItem * > & l,const SbName & name,SoType type)818 SoNodekitCatalog::addListItemType(const SbList<class CatalogItem *> & l,
819 const SbName & name, SoType type)
820 {
821 int part = this->getPartNumber(l, name);
822 if (part == SO_CATALOG_NAME_NOT_FOUND) return FALSE;
823 this->addListItemType(l, part, type);
824 return TRUE;
825 }
826
827 /*!
828 \internal
829 \since Coin 2.3
830 */
831 SbBool
hasEntry(const SbName & name) const832 SoNodekitCatalog::hasEntry(const SbName & name) const
833 {
834 if (this->getPartNumber(this->items, name) != SO_CATALOG_NAME_NOT_FOUND) return TRUE;
835 if (this->getPartNumber(this->delayeditems, name) != SO_CATALOG_NAME_NOT_FOUND) return TRUE;
836 return FALSE;
837 }
838
839 /*!
840 \internal
841 \since Coin 2.3
842 */
843 SbBool
hasListItemType(const SbName & name,SoType type) const844 SoNodekitCatalog::hasListItemType(const SbName & name, SoType type) const
845 {
846 const SbList <class CatalogItem*> * lptr = &this->items;
847 int part = this->getPartNumber(this->items, name);
848 if (part == SO_CATALOG_NAME_NOT_FOUND) {
849 lptr = &this->delayeditems;
850 part = this->getPartNumber(this->delayeditems, name);
851 }
852 const SbList <class CatalogItem*> & l = *lptr;
853 assert(part >= 0 && part < l.getLength() &&
854 "invalid part number");
855 assert(type != SoType::badType() &&
856 "dont add SoType::badType(), you stupid!");
857 assert(l[part]->islist &&
858 "type must be a list-item type" );
859
860 return l[part]->itemtypeslist.find( type ) != -1;
861 }
862
863 #endif // HAVE_NODEKITS
864