1// @configure_input@ 2 3/**************************************************************************\ 4 * Copyright (c) Kongsberg Oil & Gas Technologies AS 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * met: 10 * 11 * Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 14 * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * Neither the name of the copyright holder nor the names of its 19 * contributors may be used to endorse or promote products derived from 20 * this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33\**************************************************************************/ 34 35#ifdef HAVE_CONFIG_H 36#include <config.h> 37#endif /* HAVE_CONFIG_H */ 38 39#include <assert.h> 40#include <string.h> 41#include <sys/types.h> 42#include <sys/stat.h> 43#ifdef HAVE_DIRENT_H 44#include <dirent.h> 45#endif 46#ifdef HAVE_UNISTD_H 47#include <unistd.h> 48#endif 49#include <stdlib.h> 50 51#include <Inventor/SbPList.h> 52#include <Inventor/errors/SoDebugError.h> 53 54#include <so@gui@defs.h> 55#include <Inventor/@Gui@/SoAnyMaterialList.h> 56#include <Inventor/@Gui@/SoAny.h> 57 58struct So@Gui@MaterialListCallbackInfo { 59 So@Gui@MaterialListCB * callback; 60 void * closure; 61}; 62 63/*! 64 \class SoAnyMaterialList Inventor/@Gui@/SoAnyMaterialList.h 65 \brief The SoAnyMaterialList class is the common code for the MaterialList 66 component classes. 67*/ 68 69// ************************************************************************* 70 71/*! 72*/ 73 74SoAnyMaterialList::SoAnyMaterialList( 75 const char * const dir) 76{ 77 this->callbacks = NULL; 78 this->dirpath = NULL; 79 this->directory = NULL; 80 if (dir != NULL) 81 this->dirpath = strcpy(new char [strlen(dir)+1], dir); 82} 83 84/*! 85*/ 86 87SoAnyMaterialList::~SoAnyMaterialList( 88 void) 89{ 90 if (this->callbacks != NULL) { 91 const int num = this->callbacks->getLength(); 92 for (int i = 0; i < num; i++) 93 delete (So@Gui@MaterialListCallbackInfo *) (*this->callbacks)[i]; 94 delete this->callbacks; 95 } 96 if (this->dirpath) 97 delete [] this->dirpath; 98 if (this->directory != NULL) 99 this->freeMaterialDirectory(); 100} 101 102// ************************************************************************* 103 104/*! 105*/ 106 107void 108SoAnyMaterialList::addCallback( 109 So@Gui@MaterialListCB * const callback, 110 void * const closure) 111{ 112 if (this->callbacks == NULL) 113 this->callbacks = new SbPList; 114 So@Gui@MaterialListCallbackInfo * info = 115 new So@Gui@MaterialListCallbackInfo; 116 info->callback = callback; 117 info->closure = closure; 118 this->callbacks->append(info); 119} // addCallback() 120 121/*! 122*/ 123 124void 125SoAnyMaterialList::removeCallback( 126 So@Gui@MaterialListCB * const callback, 127 void * const closure) 128{ 129 if (! this->callbacks) { 130#if SO@GUI@_DEBUG 131 SoDebugError::postInfo("SoAnyMaterialList::removeCallback", 132 "component has zero callbacks set."); 133#endif // SO@GUI@_DEBUG 134 return; 135 } 136 137 const int numcallbacks = this->callbacks->getLength(); 138 for (int i = 0; i < numcallbacks; i++) { 139 So@Gui@MaterialListCallbackInfo * info = 140 (So@Gui@MaterialListCallbackInfo *) (*this->callbacks)[i]; 141 if (info->callback == callback && info->closure == closure) { 142 this->callbacks->remove(i); 143 delete info; 144 return; 145 } 146 } 147 148#if SO@GUI@_DEBUG 149 SoDebugError::postInfo("SoAnyMaterialList::removeCallback", 150 "callback was not set for component."); 151#endif // SO@GUI@_DEBUG 152} // removeCallback() 153 154/*! 155*/ 156 157void 158SoAnyMaterialList::invokeCallbacks( 159 SoMaterial * material) 160{ 161 if (this->callbacks) { 162 const int numCallbacks = this->callbacks->getLength(); 163 for (int i = 0; i < numCallbacks; i++) { 164 So@Gui@MaterialListCallbackInfo * info = 165 (So@Gui@MaterialListCallbackInfo *) (*this->callbacks)[i]; 166 info->callback(info->closure, (SoMaterial *) material); 167 } 168 } 169} // invokeCallbacks() 170 171// ************************************************************************* 172 173/*! 174 \internal 175 176 This method frees up the memory used by the material-index data strucure. 177*/ 178 179void 180SoAnyMaterialList::freeMaterialDirectory( 181 void) 182{ 183 if (this->directory == NULL) 184 return; 185 int i, j; 186 if ((this->directory->flags & SO@GUI@_BUILTIN_MATERIALS) == 0) { 187 // all data is allocated 188 for (i = 0; i < this->directory->numGroups; i++) { 189 for (j = 0; j < this->directory->groups[i]->numMaterials; j++) { 190 delete [] (char *) this->directory->groups[i]->materials[j]->data; 191 delete [] (char *) this->directory->groups[i]->materials[j]->name; 192 delete this->directory->groups[i]->materials[j]; 193 } 194 delete [] (char *) this->directory->groups[i]->name; 195 delete [] this->directory->groups[i]->materials; 196 delete this->directory->groups[i]; 197 } 198 delete [] this->directory->groups; 199 } else { 200 // lots of data is static and should therefore not be freed 201 for (i = 0; i < this->directory->numGroups; i++) { 202 for (j = 0; j < this->directory->groups[i]->numMaterials; j++) 203 delete this->directory->groups[i]->materials[j]; 204 delete [] this->directory->groups[i]->materials; 205 delete this->directory->groups[i]; 206 } 207 delete [] this->directory->groups; 208 } 209 delete this->directory; 210 this->directory = NULL; 211} // freeMaterialDirectory() 212 213// ************************************************************************* 214 215const char * 216SoAnyMaterialList::getMaterialDirectoryPath( 217 void) const 218{ 219 return this->dirpath; 220} // getMaterialDirectoryPath() 221 222// ************************************************************************* 223 224/*! 225 \internal 226*/ 227 228static 229int 230containsFiles( 231 const char * const path) 232{ 233#ifdef HAVE_DIRENT_H 234 DIR * directory = opendir(path); 235 struct dirent * entry; 236 struct stat entrystats; 237 if (directory != NULL) { 238 const int pathnamelen = strlen(path); 239 int foundfile = 0; 240 while (! foundfile && (entry = readdir(directory)) != NULL) { 241 if (entry->d_name[0] == '.') continue; 242 char * entrypath = new char [pathnamelen + strlen(entry->d_name) + 2]; 243 sprintf(entrypath, "%s/%s", path, entry->d_name); 244 if (stat(entrypath, &entrystats) == 0 && 245 S_ISREG(entrystats.st_mode)) { 246 delete [] entrypath; 247 closedir(directory); 248 return 1; 249 } 250 delete [] entrypath; 251 } 252 closedir(directory); 253 } 254#endif 255 return 0; 256} // containsFiles() 257 258/*! 259 \internal 260*/ 261 262char ** 263SoAnyMaterialList::getNonemptySubdirs(// static, private 264 const char * const path) 265{ 266#ifdef HAVE_DIRENT_H 267 DIR * dir = opendir(path); 268 if (! dir) return NULL; 269 270 SbPList subdirs; 271 DIR * subdir; 272 const int pathlen = strlen(path) + 2; 273 struct dirent * entry, * subentry; 274 struct stat statbuf, substatbuf; 275 while ((entry = readdir(dir)) != NULL) { 276 if (entry->d_name[0] == '.') continue; 277 int pathnamelen = pathlen + strlen(entry->d_name); 278 char * pathname = new char [ pathnamelen ]; 279 sprintf(pathname, "%s/%s", path, entry->d_name); 280 if ((stat(pathname, &statbuf) == 0) && S_ISDIR(statbuf.st_mode)) { 281 if ((subdir = opendir(pathname)) != NULL) { 282 int foundfile = 0; 283 while (! foundfile && (subentry = readdir(subdir)) != NULL) { 284 if (subentry->d_name[0] == '.') continue; 285 char * entrypathname = 286 new char [pathnamelen + strlen(subentry->d_name) + 1]; 287 sprintf(entrypathname, "%s/%s", pathname, subentry->d_name); 288 if (stat(entrypathname, &substatbuf) == 0 && 289 S_ISREG(substatbuf.st_mode)) { 290 foundfile = 1; 291 } 292 delete [] entrypathname; 293 } 294 if (foundfile) 295 subdirs.append(strcpy(new char [strlen(entry->d_name)+1], 296 entry->d_name)); 297 closedir(subdir); 298 } 299 } 300 delete [] pathname; 301 } 302 closedir(dir); 303 304 const int num = subdirs.getLength(); 305 char ** subdirarray = new char * [ num + 1 ]; 306 for (int i = 0; i < num; i++) 307 subdirarray[i] = (char *) subdirs[i]; 308 subdirarray[num] = NULL; 309 return subdirarray; 310#endif 311 return NULL; 312} // numNonemptySubdirs() 313 314 315/*! 316 \internal 317*/ 318 319char ** 320SoAnyMaterialList::getRegularFiles(// static, private 321 const char * const path) 322{ 323#ifdef HAVE_DIRENT_H 324 DIR * dir = opendir(path); 325 if (! dir) return NULL; 326 327 const int pathlen = strlen(path); 328 SbPList files; 329 struct dirent * entry; 330 while ((entry = readdir(dir)) != NULL) { 331 if (entry->d_name[0] == '.') continue; 332 char * pathname = new char [pathlen + 2 + strlen(entry->d_name)]; 333 sprintf(pathname, "%s/%s", path, entry->d_name); 334 struct stat statbuf; 335 if ((stat(pathname, &statbuf) == 0) && S_ISREG(statbuf.st_mode)) 336 files.append(strcpy(new char [strlen(entry->d_name)+1], 337 entry->d_name)); 338 delete [] pathname; 339 } 340 closedir(dir); 341 342 const int num = files.getLength(); 343 char ** filearray = new char * [ num + 1 ]; 344 for (int i = 0; i < num; i++) 345 filearray[i] = (char *) files[i]; 346 filearray[num] = NULL; 347 return filearray; 348#endif 349 return NULL; 350} // getRegularFiles() 351 352/*! 353 \internal 354 355 Used for qsort(). 356*/ 357 358int 359SoAnyMaterialList::qsort_comparator(// static, private 360 const void * itemA, 361 const void * itemB) 362{ 363 SO@GUI@_STUB(); 364 return 0; 365} // qsort_comparator() 366 367/*! 368*/ 369 370So@Gui@MaterialDirectory * 371SoAnyMaterialList::getMaterialDirectory( 372 void) 373{ 374 if (this->directory != NULL) // already created 375 return this->directory; 376 377 // get the path to search for materials in 378 SbString path; 379 if (this->dirpath) { path = this->dirpath; } 380 381 const char * envvars[] = { 382 // FIXME: where the hell does the "WALLET" env-var come from? 383 // 20020109 mortene. 384 "SO_MATERIAL_DIR", "WALLET", "COIN_HOME" 385 }; 386 387 for (unsigned int i=0; i < (sizeof(envvars) / sizeof(char *)); i++) { 388 if (path.getLength() == 0) { 389 const char * p = SoAny::si()->getenv(envvars[i]); 390 if (p) { path = p; } 391 } 392 } 393 394 if (path.getLength() > 0) { path += "/materials"; } 395 else { 396 const char * p = SoAny::si()->getenv("OIVHOME"); 397 if (p) { 398 path = p; 399 // FIXME: use DIRSEP string? ????-??-?? larsa. 400 path += "/data/materials"; 401 } 402 } 403 404 this->directory = new So@Gui@MaterialDirectory; 405 this->directory->flags = 0; 406 this->directory->current = 0; 407 408 SbBool founddiskmaterials = FALSE; 409 410 if (path.getLength() > 0) { 411#if SO@GUI@_DEBUG && 0 // debug 412 SoDebugError::postInfo("SoAnyMaterialList::getMaterialDirectory", 413 "scanning '%s'", path.getString()); 414#endif // debug 415 char ** subdirs = SoAnyMaterialList::getNonemptySubdirs(path.getString()); 416 if (subdirs) { 417 founddiskmaterials = TRUE; 418 int numsubdirs; 419 for (numsubdirs = 0; subdirs[numsubdirs] != NULL; numsubdirs++) { } 420 this->directory->numGroups = numsubdirs; 421 this->directory->groups = new So@Gui@MaterialGroup * [ numsubdirs ]; 422 for (int subdir = 0; subdir < numsubdirs; subdir++) { 423 this->directory->groups[subdir] = new So@Gui@MaterialGroup; 424 this->directory->groups[subdir]->name = subdirs[subdir]; 425 char * subdirname = 426 new char [strlen(path.getString()) + strlen(subdirs[subdir]) + 2]; 427 sprintf(subdirname, "%s/%s", path.getString(), subdirs[subdir]); 428 char ** files = SoAnyMaterialList::getRegularFiles(subdirname); 429 assert(files != NULL); 430 int numfiles; 431 for (numfiles = 0; files[numfiles] != NULL; numfiles++) { } 432 this->directory->groups[subdir]->numMaterials = numfiles; 433 this->directory->groups[subdir]->materials = 434 new So@Gui@Material * [ numfiles ]; 435 for (int file = 0; file < numfiles; file++) { 436 this->directory->groups[subdir]->materials[file] = 437 new So@Gui@Material; 438 this->directory->groups[subdir]->materials[file]->name = 439 files[file]; 440 char * buf = new char [strlen(subdirname) + strlen(files[file]) + 2]; 441 sprintf(buf, "%s/%s", subdirname, files[file]); 442 this->directory->groups[subdir]->materials[file]->data = buf; 443 } 444 delete [] files; // actual strings are transfered, don't delete them! 445 delete [] subdirname; 446 } 447 delete [] subdirs; // actual strings are transfered, don't delete them! 448 } 449 } 450 451 // fallback on builtins 452 if (!founddiskmaterials) { this->setupBuiltinMaterials(this->directory); } 453 return this->directory; 454} // getMaterialDirectory() 455 456// ************************************************************************* 457 458