/* * * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ /* * Copyright (C) 1991-93 Silicon Graphics, Inc. * _______________________________________________________________________ ______________ S I L I C O N G R A P H I C S I N C . ____________ | | $Revision: 1.1 $ | | Classes: | SoXtMaterialList | | Author(s): David Mott | | ______________ S I L I C O N G R A P H I C S I N C . ____________ _______________________________________________________________________ */ #include #include #include #include #include #include #include #include #ifdef _POSIX_SOURCE extern "C" char *strdup(const char *s); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *bogusFile = ""; #define TOGGLE_ON(BUTTON) \ XmToggleButtonSetState((Widget) BUTTON, TRUE, FALSE); #define TOGGLE_OFF(BUTTON) \ XmToggleButtonSetState((Widget) BUTTON, FALSE, FALSE); // // returns true if the passed file is a subdirectory in the current directory. // (call chdir() before calling this). // static SbBool isDirectory(char *file) { struct stat buf; SbBool is_dir = FALSE; if (stat(file, &buf) == 0) if ((buf.st_mode & S_IFMT) == S_IFDIR) is_dir = 1; return (is_dir); } // // this returns TRUE if the file contains a material // static SbBool isMtlFile(char *filename) { // Make sure it's not . or .. if (filename[0] == '.') return FALSE; // read the file, search for SoMaterial SbBool fileHasMtl = FALSE; SoInput in; SoNode *root; if (in.openFile(filename)) { if (SoDB::read(&in, root)) { root->ref(); // find the material node SoSearchAction sa; sa.setType(SoMaterial::getClassTypeId()); sa.apply(root); fileHasMtl = (sa.getPath() != NULL); root->unref(); } } return (fileHasMtl); } // // Fill in palette with material files // static void createStringTable(char *dir, XmString **table, int *size) { XmString *tbl; int numFiles = 0; // build the list of files struct dirent *direntry; DIR *dirp; char *f; if (dirp = opendir(dir)) { char currentDir[MAXPATHLEN]; getwd(currentDir); chdir(dir); // count the number of files so we can allocate space in the str table while (direntry = readdir(dirp)) { f = direntry->d_name; if (isMtlFile(f)) numFiles++; } (void) closedir(dirp); // now build the string table if (numFiles != 0) { // get space for the table tbl = (XmString *) malloc(numFiles * sizeof(XmString)); *table = tbl; *size = numFiles; // fill the table in dirp = opendir(dir); chdir(dir); while (direntry = readdir(dirp)) { f = direntry->d_name; if (isMtlFile(f)) { *tbl = SoXt::encodeString(f); tbl++; } } (void) closedir(dirp); } else { // get space for one entry - bogusFile tbl = (XmString *) malloc(sizeof(XmString)); *table = tbl; *size = 1; *tbl = SoXt::encodeString(bogusFile); } chdir(currentDir); // back to our working directory } #ifdef DEBUG else { SoDebugError::post("SoXtMaterialList::createStringTable", "Cannot open directory %s.", dir); } #endif } static void destroyStringTable(XmString *table, int size) { int i; // nuke the table entries for (i = 0; i < size; i++) XmStringFree(table[i]); free(table); } //////////////////////////////////////////////////////////////////////// // // Public constructor - build the widget right now // SoXtMaterialList::SoXtMaterialList( Widget parent, const char *name, SbBool buildInsideParent, const char *dir) : SoXtComponent( parent, name, buildInsideParent) // //////////////////////////////////////////////////////////////////////// { // In this case, this component is what the app wants, so buildNow = TRUE constructorCommon(dir, TRUE); } //////////////////////////////////////////////////////////////////////// // // SoEXTENDER constructor - the subclass tells us whether to build or not // SoXtMaterialList::SoXtMaterialList( Widget parent, const char *name, SbBool buildInsideParent, const char *dir, SbBool buildNow) : SoXtComponent( parent, name, buildInsideParent) // //////////////////////////////////////////////////////////////////////// { // In this case, this component may be what the app wants, // or it may want a subclass of this component. Pass along buildNow // as it was passed to us. constructorCommon(dir, buildNow); } //////////////////////////////////////////////////////////////////////// // // Called by the constructors // // private // void SoXtMaterialList::constructorCommon(const char *dir, SbBool buildNow) // ////////////////////////////////////////////////////////////////////// { setClassName("SoXtMaterialList"); if (dir != NULL) materialDir = strdup(dir); else materialDir = strdup("/usr/share/data/materials"); callbackList = new SoCallbackList; curPalette = -1; // Build the widget tree, and let SoXtComponent know about our base widget. if (buildNow) { Widget w = buildWidget(getParentWidget()); if (w != NULL) setBaseWidget(w); } } //////////////////////////////////////////////////////////////////////// // // Destructor. // SoXtMaterialList::~SoXtMaterialList() // //////////////////////////////////////////////////////////////////////// { delete callbackList; if (materialDir != NULL) free(materialDir); for (int i = 0; i < mtlPalettes.getLength(); i++) free((char *) mtlPalettes[i]); } //////////////////////////////////////////////////////////////////////// // // Get the dir name from the env variable, and set up the palettes. // This returns TRUE if it did so without error, and sets the member // variable curPalette to 0. Else this returns FALSE and sets // curPalette to -1. // // private // SbBool SoXtMaterialList::setupPalettes() // //////////////////////////////////////////////////////////////////////// { // see if SO_MATERIAL_DIR has changed. char *envDir = getenv("SO_MATERIAL_DIR"); if (envDir != NULL) { if ((strcmp(envDir, materialDir) == 0) && (curPalette != -1)) return TRUE; // env dir is same as current dir, palette is already OK if (materialDir != NULL) free(materialDir); materialDir = strdup(envDir); } else if (curPalette != -1) return TRUE; // env dir not set, but palette is already OK // read the palette! curPalette = 0; // build the list of directories struct dirent *direntry; DIR *dirp; char *f; if (dirp = opendir(materialDir)) { char currentDir[MAXPATHLEN]; getwd(currentDir); chdir(materialDir); while (direntry = readdir(dirp)) { f = direntry->d_name; /* hide '.' files */ if (f[0] != '.') { if (isDirectory(f)) { mtlPalettes.append(strdup(f)); } } } (void) closedir(dirp); chdir(currentDir); // back to our working directory } else { #ifdef DEBUG SoDebugError::post("SoXtMaterialList::setupPalettes", "Cannot open directory %s. Try setting the environment variable SO_MATERIAL_DIR to a directory which has material files in it.", materialDir); #endif curPalette = -1; } return (curPalette != -1); // if not -1, return TRUE } //////////////////////////////////////////////////////////////////////// // // This builds the pulldown menu, using materialDir as the parent // directory. // // usage: private // Widget SoXtMaterialList::buildPulldownMenu(Widget parent) // //////////////////////////////////////////////////////////////////////// { Widget menubar, pulldown; Widget submenu; Arg args[5]; int n; n = 0; XtSetArg(args[n], XmNuserData, this); n++; menubar = XmCreateMenuBar(parent, "menuBar", NULL, 0); pulldown = XmCreatePulldownMenu(menubar, "controlPulldown", args, n); n = 0; XtSetArg(args[n], XmNsubMenuId, pulldown); n++; submenu = XtCreateManagedWidget("Palettes", xmCascadeButtonGadgetClass, menubar, args, n); #define MENU_ITEM(BUTTON,NAME,KONST) \ n = 0; \ XtSetArg(args[n], XmNuserData, this); n++; \ XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY); n++; \ BUTTON = XtCreateManagedWidget(NAME, \ xmToggleButtonGadgetClass, \ pulldown,args,n); \ XtAddCallback(BUTTON, XmNvalueChangedCallback, \ (XtCallbackProc) SoXtMaterialList::menuPick, \ (XtPointer) KONST); // create the menu items ???pass in the dir names! Widget button; menuItems.truncate(0); for (int i = 0; i < mtlPalettes.getLength(); i++) { char *dir = (char *) mtlPalettes[i]; MENU_ITEM(button, dir, i) menuItems.append(button); // store these buttons for future reference } #undef MENU_ITEM if (menuItems.getLength() == 0) { #ifdef DEBUG SoDebugError::post("SoXtMaterialList::buildPulldownMenu", "Directory %s has no material palettes.", materialDir); #endif curPalette = -1; } XtManageChild(submenu); // set the current palette toggle to on if (curPalette != -1) TOGGLE_ON(menuItems[curPalette]); return menubar; } //////////////////////////////////////////////////////////////////////// // // Fill in the Motif list with file names from the current palette. // // usage: virtual private // void SoXtMaterialList::fillInMaterialList() // //////////////////////////////////////////////////////////////////////// { if (curPalette == -1) return; XmString *table; int count; Arg args[2]; int n; char curdir[256]; // create a string table for the current palette directory sprintf(curdir, "%s/%s", materialDir, (char *)mtlPalettes[curPalette]); createStringTable(curdir, &table, &count); n = 0; XtSetArg(args[n], XmNitems, table); n++; XtSetArg(args[n], XmNitemCount, count); n++; XtSetValues(mtlList, args, n); /* free our local table - XmCreateScrolledList made a copy */ destroyStringTable(table, count); } //////////////////////////////////////////////////////////////////////// // // This routine builds all the widgets, sets up callback routines, // and does the layout using motif. // // usage: protected // Widget SoXtMaterialList::buildWidget(Widget parent) // //////////////////////////////////////////////////////////////////////// { // setup palette if needed. return NULL on error. if (! setupPalettes()) return NULL; Widget menubar; int n; Arg wargs[10]; // create the top level widget Widget widget = XtCreateWidget(getWidgetName(),xmFormWidgetClass,parent,NULL,0); // build the subcomponents menubar = buildPulldownMenu(widget); if (curPalette == -1) { XtDestroyWidget(menubar); XtDestroyWidget(widget); widget = NULL; return NULL; } XmString *table; int count; char curdir[256]; // create a string table for the current palette directory sprintf(curdir, "%s/%s", materialDir, (char *)mtlPalettes[curPalette]); createStringTable(curdir, &table, &count); // Layout n = 0; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetValues(menubar, wargs, n); //??? BUG IN LIST WIDGET - I cannot build the widget, and then // call XtSetValues to set up the layout. I must do it all at once! // This seems to me like a bug in motif! n = 0; XtSetArg(wargs[n], XmNitems, table); n++; XtSetArg(wargs[n], XmNitemCount, count); n++; XtSetArg(wargs[n], XmNvisibleItemCount, 8); n++; XtSetArg(wargs[n], XmNselectionPolicy, XmSINGLE_SELECT); n++; XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(wargs[n], XmNtopWidget, menubar); n++; //???XtSetValues(mtlList, wargs, n); mtlList = XmCreateScrolledList(widget, "materialList", wargs, n); XtAddCallback(mtlList, XmNsingleSelectionCallback, (XtCallbackProc) SoXtMaterialList::listPick, (XtPointer) this); /* free our local table - XmCreateScrolledList made a copy */ destroyStringTable(table, count); /* manage those children! */ XtManageChild(mtlList); XtManageChild(menubar); return widget; } //////////////////////////////////////////////////////////////////////// // // Called by Xt when a menu item is picked. // // Use: static private // void SoXtMaterialList::menuPick(Widget w, int id, XtPointer) // //////////////////////////////////////////////////////////////////////// { SoXtMaterialList *ml; Arg args[1]; XtSetArg(args[0], XmNuserData, &ml); XtGetValues(w, args, 1); #ifdef DEBUG if (ml == NULL) { SoDebugError::post("SoXtMaterialList::menuPick", "ml is NULL"); return; } #endif if (id != ml->curPalette) { // toggle the previous current palette item to OFF TOGGLE_OFF(ml->menuItems[ml->curPalette]); ml->curPalette = id; // and fill in the mtlList with the new current palette ml->fillInMaterialList(); } // make sure the current palette item is set to ON TOGGLE_ON(ml->menuItems[ml->curPalette]); } //////////////////////////////////////////////////////////////////////// // // Called by Xt when a list item is picked. // // Use: static private // void SoXtMaterialList::listPick(Widget, SoXtMaterialList *ml, XtPointer p) // //////////////////////////////////////////////////////////////////////// { XmListCallbackStruct *listdata = (XmListCallbackStruct *) p; char *name; char filename[256]; name = SoXt::decodeString(listdata->item); if (strcmp(name, bogusFile) == 0) return; // construct the file name from all our information sprintf(filename, "%s/%s/%s", ml->materialDir, (char *) ml->mtlPalettes[ml->curPalette], name); // read the file, search for SoMaterial, and pass data to callbacks SoInput in; SoNode *root; if (in.openFile(filename)) { if (SoDB::read(&in, root)) { root->ref(); // find the material node SoSearchAction sa; SoFullPath *fullPath; // Look for existing camera sa.setType(SoMaterial::getClassTypeId()); sa.apply(root); fullPath = (SoFullPath *) sa.getPath(); if (fullPath != NULL) { // invoke the callbacks with this material! fullPath->ref(); SoMaterial *mtl = (SoMaterial *) fullPath->getTail(); ml->callbackList->invokeCallbacks(mtl); fullPath->unref(); } #ifdef DEBUG else { SoDebugError::post("SoXtMaterialList::listPick", "%s has no Material node.", filename); } #endif root->unref(); } #ifdef DEBUG else { SoDebugError::post("SoXtMaterialList::listPick", "Cannot read file %s.", filename); } #endif } #ifdef DEBUG else { SoDebugError::post("SoXtMaterialList::listPick", "Cannot open file %s.", filename); } #endif free(name); } // // redefine those generic virtual functions // const char * SoXtMaterialList::getDefaultWidgetName() const { return "SoXtMaterialList"; } const char * SoXtMaterialList::getDefaultTitle() const { return "Material List"; } const char * SoXtMaterialList::getDefaultIconTitle() const { return "Mat List"; }