home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Audio-DSP / NU / Source / GlyphManager.m < prev    next >
Encoding:
Text File  |  1993-03-07  |  17.5 KB  |  603 lines

  1. #import "GlyphManager.h"
  2. #import "PrefsManager.h"
  3. #import "Glyph.h"
  4. #import <appkit/Application.h>
  5. #import <appkit/NXBrowser.h>
  6. #import <appkit/NXBrowserCell.h>
  7. #import <appkit/Matrix.h>
  8. #import <appkit/TextField.h>
  9. #import <appkit/ScrollView.h>
  10. #import <appkit/Panel.h>
  11. #import <defaults/defaults.h>
  12. #import <appkit/Font.h>
  13. #import <sys/file.h>
  14. #import <stdlib.h>
  15. #import <strings.h>
  16. #import <NXCType.h>
  17. #import <sys/dir.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #import "MenuManager.h"
  21. #import "ClassManager.h"
  22. #import "GlyphView.h"
  23. #import "WorkspaceManager.h"
  24. #include <sys/types.h>
  25. #include <sys/dir.h>
  26.  
  27. extern id Nu ;
  28. extern char bigBuf[] ; // a "big" buffer for general use
  29.  
  30.  
  31.  
  32. void
  33. freeFileList(struct direct ** aList, int knt)
  34. { // free up a filelist created by scandir
  35.   int i ;
  36.   if(aList)
  37.   { for(i = 0 ; i < knt ; i++) // free up former struct
  38.       free(aList[i]) ; 
  39.     free(aList) ;
  40.     aList = NULL ;
  41.   }
  42. }
  43.  
  44. int isDotM(ptr)
  45. struct direct *ptr ;
  46. { // returns 1 iff ptr has a .m extension.
  47.   // We don't use rindex because nothing
  48.   // guarantees ptr is null-terminated!
  49.   if(ptr == NULL)
  50.     return 0 ;
  51.   if(ptr->d_name[ptr->d_namlen - 1] == 'm' &&
  52.      ptr->d_name[ptr->d_namlen - 2] == '.')
  53.     return 1 ;
  54.   return 0 ;
  55. }
  56.  
  57. int isDotWsd(ptr)
  58. struct direct *ptr ;
  59. { // returns 1 iff ptr has a .wsd extension.
  60.   // Currently, don't use this, but would like
  61.   // to incomporate wsd file access into
  62.   // the this manager.
  63.   if(ptr == NULL)
  64.     return 0 ;
  65.   
  66.   if(ptr->d_name[ptr->d_namlen - 1] == 'd' &&
  67.      ptr->d_name[ptr->d_namlen - 2] == 's' &&
  68.      ptr->d_name[ptr->d_namlen - 3] == 'w' &&
  69.      ptr->d_name[ptr->d_namlen - 4] == '.')
  70.     return 1 ;
  71.   return 0 ;
  72. }
  73.  
  74. int hasNoExt(ptr) 
  75. struct direct *ptr ;
  76. { // returns 1 iff ptr has no extension
  77.   if(!index(ptr->d_name,'.'))
  78.     return 1 ;
  79.   return 0 ;
  80. }
  81.  
  82. int
  83. selectFiles(ptr)
  84. struct direct *ptr ;
  85. { // returns 1 iff the entry has a .m or .wsd extension, or
  86.   // no extension at all.  else returns 0.
  87.   if(hasNoExt(ptr) || isDotM(ptr))
  88.     return 1 ;
  89.   else
  90.     return 0 ;
  91. }
  92.  
  93.  
  94. @implementation GlyphManager: Object
  95.  
  96. + (BOOL) putPathTo: (char *) aFile startingAt: (char *) aPath  in: (char *)  thePath;
  97. { // Search for file (or directory aFile.  Start the search in the
  98.   // directory aPath.  If found, return YES and put path in thePath.
  99.   // else return NO.
  100.   int i, fileCnt, nextPathLen ;
  101.   struct stat statBuf ;
  102.   struct direct **filesList ;
  103.   BOOL rval = NO ; 
  104.   fileCnt = scandir(aPath,&filesList,NULL,NULL) ;
  105.   for(i = 0 ; i < fileCnt ; i++)
  106.   { if(!strcmp(aFile, (*filesList[i]).d_name)) 
  107.     { // if our file...
  108.       sprintf(thePath,"%s/%s",aPath,aFile) ;
  109.       rval = YES ;
  110.       break ;
  111.     }
  112.     nextPathLen = strlen(aPath) + strlen((*filesList[i]).d_name) + 2 ;
  113.     if(strcmp((*filesList[i]).d_name,".") && strcmp((*filesList[i]).d_name,".."))
  114.     { // examine files, skipping "." and ".."
  115.       char nextPath[nextPathLen] ;
  116.       sprintf(nextPath,"%s/%s",aPath,(*filesList[i]).d_name) ;
  117.       stat(nextPath,&statBuf) ;
  118.       if(!(statBuf.st_mode & 0x8000)) // if a directory...
  119.       { if(rval = [self putPathTo: aFile startingAt: nextPath in: thePath])
  120.           break ;
  121.       }
  122.     }
  123.   }
  124.   for(i = 0 ; i < fileCnt ; i++)
  125.     free(filesList[i]) ;
  126.   free(filesList) ;
  127.   return rval ;
  128. }
  129.  
  130.  
  131. - browser ;
  132. { return browser ;
  133. }
  134.  
  135. - (int)browser:sender fillMatrix:matrix inColumn:(int)column ;
  136. { if(!rootPath) 
  137.     rootPath = NXGetDefaultValue([NXApp appName],"NuPath") ;
  138.   listedColumn = column ; 
  139.   freeFileList(fileList,fileKnt) ; // free up former struct
  140.   if(column == 0)
  141.   { fileKnt = scandir(rootPath,&fileList,selectFiles,alphasort) ;
  142.     return fileKnt ;
  143.   }
  144.   else
  145.   { char aPath[1024] ;
  146.     [sender getPath:aPath toColumn:column] ;
  147.     sprintf(bigBuf,"%s%s",rootPath,aPath) ;
  148.     fileKnt = scandir(bigBuf,&fileList,selectFiles,alphasort) ;
  149.     return fileKnt ;
  150.   }
  151. }
  152.  
  153. - browser:sender loadCell:cell atRow:(int)row inColumn:(int)column ;
  154. { if(column != listedColumn)
  155.     [self browser: browser fillMatrix:nil inColumn: column] ;
  156.   if(isDotM(fileList[row]))
  157.   { strcpy(bigBuf,(*fileList[row]).d_name) ;
  158.     bigBuf[(*fileList[row]).d_namlen - 2] = '\0' ;
  159.     [cell setStringValue: bigBuf] ;
  160.     [cell setLeaf: YES] ;
  161.   }
  162.   else
  163.   { [cell setLeaf: NO] ;
  164.     [cell setStringValue: (*fileList[row]).d_name] ;
  165.   }
  166.   return self ;
  167. }
  168.  
  169. - browserHit:sender ;
  170. { NXEvent *anEvent ;
  171.   anEvent = [NXApp currentEvent] ;
  172.   if(anEvent->type == NX_LMOUSEUP &&
  173.     anEvent->data.mouse.click == 2) // if a double-click..
  174.     [self instantiate:sender] ;
  175.   return self ;
  176. }
  177.  
  178. - decHeight:sender
  179. { [[height cell] setFloatValue: [[height cell] floatValue] - 1.0] ; 
  180.   if([coupler state])
  181.     [[width cell] setFloatValue:  [[height cell] floatValue]] ;
  182.   return self;
  183. }
  184.  
  185. - decWidth:sender
  186. { [[width cell] setFloatValue: [[width cell] floatValue] - 1.0] ;
  187.   if([coupler state])
  188.     [[height cell] setFloatValue:  [[width cell]floatValue]] ;
  189.   return self;
  190. }
  191.  
  192. - delete:sender ;
  193. { // delete the selected class or category.  a category
  194.   // can only be deleted if it is empty, i.e. it has
  195.   // no classes or subcategories.
  196.   // NOTE: unload deleted classes?
  197.   char aPath[512] ;
  198.   int rval, selCol, selRow ;
  199.   if((selCol = [browser selectedColumn]) == -1)
  200.     return self ;
  201.   selRow = [[browser matrixInColumn: selCol] selectedRow] ;
  202.   if(![self isDotMCol:selCol row:selRow]) // remove category
  203.   { // be sure category is empty
  204.     { struct direct **aList ;
  205.       [browser getPath: aPath toColumn: selCol+1] ;     
  206.       sprintf(bigBuf,"%s%s",rootPath,aPath) ;
  207.       rval = scandir(bigBuf,&aList,selectFiles,alphasort) ;
  208.       freeFileList(aList,rval) ;
  209.       if(rval > 0)
  210.       { NXRunAlertPanel("Nu","Error: can't delete non-empty category",
  211.            NULL,NULL,NULL) ;
  212.         return self ;
  213.       }
  214.     }
  215.     rval = NXRunAlertPanel("Nu",
  216.      "Delete category %s?","Yes","No",NULL, aPath) ;
  217.     if(rval == 0)
  218.       return self ;
  219.     sprintf(bigBuf,"rmdir %s%s > /dev/null",rootPath,aPath) ;
  220.     system(bigBuf) ;
  221.   }
  222.   else // remove class
  223.   { [browser getPath: aPath toColumn: selCol] ;     
  224.     rval = NXRunAlertPanel("Nu",
  225.      "Delete class %s in category %s?","Yes","No",NULL,
  226.        [[[browser matrixInColumn: selCol] selectedCell] stringValue],aPath);
  227.       if(rval == 0)
  228.         return self ;
  229.      [browser getPath: aPath toColumn: selCol+1] ;     
  230.      sprintf(bigBuf,"rm %s%s.m 2> /dev/null",rootPath,aPath) ;
  231.      system(bigBuf) ;
  232.      sprintf(bigBuf,"rm %s/.h/%s.h 2> /dev/null", rootPath,
  233.          [[[browser matrixInColumn: selCol] selectedCell] stringValue]) ;
  234.      system(bigBuf) ;
  235.      sprintf(bigBuf,"rm %s/.o/%s.o 2> /dev/null", rootPath,
  236.          [[[browser matrixInColumn: selCol] selectedCell] stringValue]) ;
  237.      system(bigBuf) ;
  238.      sprintf(bigBuf,"rm %s/.g/%s.g 2> /dev/null", rootPath,
  239.          [[[browser matrixInColumn: selCol] selectedCell] stringValue]) ;
  240.      system(bigBuf) ;
  241.   }
  242.   [[browser window] disableFlushWindow] ;
  243.   [browser loadColumnZero] ;
  244.   sprintf(bigBuf,"/%s",aPath) ;
  245.   [browser setPath: bigBuf] ;
  246.   [[browser window] reenableFlushWindow] ;
  247.   [[browser window] flushWindow] ;
  248.   return self ;
  249. }
  250.  
  251. - edit:sender
  252. { // This is invoked from the "inspect" button (which was
  253.   // originally called "edit"...hence the method name).
  254.   int selCol, selRow ;
  255.   char aPath[512] ;
  256.   selCol = [browser selectedColumn]  ;
  257.   selRow = [[browser matrixInColumn: selCol] selectedRow] ;
  258.   [browser getPath: aPath toColumn: selCol+1] ;
  259.   sprintf(bigBuf,"%s%s.m",rootPath,aPath) ;
  260.   if([self isDotMCol:selCol row:selRow])
  261.     [self editFile: bigBuf 
  262.          class: (char *) 
  263.           [[[browser matrixInColumn: selCol] selectedCell] stringValue]] ;
  264.   return self ;
  265. }
  266.  
  267.  
  268. - editFile: (char *) aFile class: (char *) aClass ;
  269. { // aClass is the name of a class, aFile is the
  270.   // full pathname of a class definition, including
  271.   // the .m extension.  Attempt
  272.   // to open the definition in a class inspector window.
  273.   // If aFile is NULL, search for aClass.  If
  274.   // aClass is NULL, parse the class name from
  275.   // aFile by removing the path and the extension.
  276.   // If both aClass and aFile are NULL, do nothing!
  277.   // Returns nil if class definition can't be found,
  278.   // else returns self.
  279.   id winList, aWin, manager ;
  280.   int i,knt ;
  281.   const char *theFlags ;
  282.   struct stat statBuf ;
  283.   if(!aFile && !aClass)
  284.     return self ; // do nothing!
  285.   if(!aClass) // no class name: get it from aFile
  286.   { int len ;
  287.     char *nameStart ;  
  288.     nameStart = rindex(aFile,'/') ;
  289.     if(!nameStart)
  290.       return nil ;
  291.     else
  292.        nameStart++ ;
  293.     len = strlen(nameStart) - 2 ; // -2 for .m at end
  294.     aClass = alloca(len + 1) ;
  295.     strncpy(aClass,nameStart,len) ;
  296.     aClass[len] = '\0' ;
  297.   }
  298.   else if(!aFile)
  299.   { // search for aFile
  300.     char classExtended[128] ;
  301.     // add extension to aClass
  302.     sprintf(classExtended,"%s.m",aClass) ;
  303.     // now search for the file
  304.     aFile = alloca(2048) ;
  305.     if(![GlyphManager putPathTo: classExtended 
  306.           startingAt: (char *) NXGetDefaultValue([NXApp appName],"NuPath") 
  307.           in: aFile])
  308.       return nil ;
  309.   }
  310.   // do we allow multiple windows of same class?
  311.   theFlags = NXGetDefaultValue([NXApp appName],"Flags") ;
  312.   if(theFlags[MULTIPLECLASSWINDOWS]  == '1')
  313.   { // check if Class is already being edited
  314.     winList = [NXApp windowList] ;
  315.     knt = [winList count] ;
  316.     for(i = 0 ; i < knt ; i++)
  317.     { aWin = [winList objectAt: i] ;
  318.       if(!strcmp([aWin name],aClass)) // found it...
  319.         return [aWin makeKeyAndOrderFront: self] ;
  320.     }
  321.   }
  322.   // verify that the file exists
  323.   if(stat(aFile,&statBuf))
  324.     return nil ;
  325. //  [Nu printf: "non nil\m"] ;
  326.   // open class manager on this file
  327.   manager = [NXApp loadNibSection:"ClassManager.nib" 
  328.       owner: self] ;
  329.   [manager init] ; // this is supposed to happen automatically
  330.   // as part of loadNibSection:, but it don't! So I send init
  331.   // explicitly
  332.   [manager fileName: aFile] ;
  333.   [manager className: aClass] ; 
  334.   [manager readFile] ;
  335.   [manager display] ;
  336.   [manager makeKeyAndOrderFront: self] ;
  337.   return self ;
  338. }
  339.  
  340. - enterHeight:sender
  341. { // user has entered height ; if coupled, copy into width
  342.   if([coupler state])
  343.     [[width cell] setFloatValue:  [[height cell] floatValue]] ;
  344.     return self;
  345. }
  346.  
  347. - enterWidth:sender
  348. { // user has entered width ; if coupled, copy into height
  349.   if([coupler state])
  350.     [[height cell] setFloatValue:  [[width cell]floatValue]] ;
  351.     return self;
  352. }
  353.  
  354. - glyphView ;
  355. { return glyphView ;
  356. }
  357.  
  358. - incHeight:sender
  359. { [[height cell] setFloatValue: [[height cell] floatValue] + 1.0] ;
  360.   if([coupler state])
  361.     [[width cell] setFloatValue:  [[height cell] floatValue]] ;
  362.   return self;
  363. }
  364.  
  365. - incWidth:sender
  366. { [[width cell] setFloatValue: [[width cell] floatValue] + 1.0] ;
  367.   if([coupler state])
  368.     [[height cell] setFloatValue:  [[width cell]floatValue]] ;
  369.   return self ;
  370. }
  371.  
  372. - instantiate:sender ;
  373. { // instantiate the currently selected class
  374.   // and add it to our glyphView.  If the
  375.   // glyph returns nil in response to width:height:,
  376.   // then it isn't added to the glyphView.
  377.   char className[128], classPath[256], oFile[256] ;
  378.   int selCol, selRow ;
  379.   id aGlyphClass ;
  380.   Glyph * aGlyph ;
  381.   selCol = [browser selectedColumn]  ;
  382.   selRow = [[browser matrixInColumn: selCol] selectedRow] ;
  383.   if([self isDotMCol:selCol row:selRow])
  384.   { struct stat mStat,oStat ;
  385.     strcpy(className,[[[browser matrixInColumn: selCol
  386.                   ]selectedCell] stringValue]) ;
  387.     // may need to compile file
  388.     [browser getPath: classPath toColumn: selCol + 1] ;
  389.     sprintf(bigBuf,"%s%s.m",rootPath,classPath) ;
  390.     stat(bigBuf,&mStat) ;
  391.     sprintf(oFile,"%s/.o/%s.o",rootPath,className) ;
  392.     if(stat(oFile,&oStat) || (mStat.st_mtime >= oStat.st_mtime)) 
  393.     {  [msgView setStringValue: "Compiling..."] ;
  394.        NXPing() ;
  395.        if(![ClassManager compileClass: className file: bigBuf])
  396.        {  [msgView setStringValue: ""] ;
  397.           return self ; 
  398.        }
  399.        [msgView setStringValue: "Loading..."] ;
  400.        NXPing() ;
  401.        if(![ClassManager load: className])
  402.        { [msgView setStringValue: ""] ;
  403.          return self ;
  404.        }
  405.     }
  406.     else if([ClassManager loadIndex:className] == -1)
  407.     { [msgView setStringValue: "Loading..."] ;
  408.        NXPing() ;
  409.        if(![ClassManager load: className])
  410.        { [msgView setStringValue: ""] ;
  411.          return self ;
  412.        }
  413.     }
  414.     [msgView setStringValue: "Instantiating..."] ;
  415.     NXPing() ;
  416.     aGlyphClass = objc_getClass(className) ;
  417.     aGlyph = [[aGlyphClass alloc]
  418.         init: [[width cell] floatValue]
  419.       :[[height cell] floatValue]] ;
  420.     if(aGlyph)
  421.     { NXRect aRect ; 
  422.       aRect.origin.x = aRect.origin.y = 0.0 ;
  423.       [[glyphView superview] getDocVisibleRect: &aRect] ;
  424.       [aGlyph moveTo: aRect.origin.x :aRect.origin.y]  ;
  425.       [[glyphView rootGlyph] plant: aGlyph] ;
  426.       [aGlyph getFrame: &aRect] ;
  427.       [[glyphView rootGlyph] display: &aRect from: nil] ;
  428.     }
  429.   }
  430.   [msgView setStringValue: ""] ;
  431.   return self ;
  432. }
  433.  
  434. - (BOOL) isDotMCol: (int) col row:(int) row ;
  435. { // returns YES iff file in browser at col,row is
  436.   // a dot M file.  The decision is made by
  437.   // examining the entries first char: if it
  438.   // is upper case, then its assumed to be a .m file
  439.   const char *fName ;
  440.   fName = [[[browser matrixInColumn: col] cellAt: row :0] stringValue] ;
  441.   if(fName)
  442.     return NXIsUpper((unsigned) fName[0]) ;
  443.   else
  444.     return NO ;
  445. }
  446.  
  447. - nameTextCell ;
  448. { // return id of cell in widget labelled Name:
  449.   return [name cell] ;
  450. }
  451.  
  452. - newCat:sender 
  453. { char aPath[512], newName[128] ;
  454.   int selCol, selRow ;
  455.   strcpy(newName,[[name cell] stringValue]) ;
  456.   if(newName[0] == '\0') // nothing there !
  457.     return self ; 
  458.   if(NXIsUpper((unsigned) newName[0]))
  459.   { NXRunAlertPanel("Nu","Error: categories must not begin capitalized",
  460.      NULL,NULL,NULL) ;
  461.     return self ;
  462.   }
  463.   if((selCol = [browser selectedColumn]) != -1)
  464.   { selRow = [[browser matrixInColumn: selCol] selectedRow] ;
  465.     if(fileList[0]) // if not empty column
  466.       if([self isDotMCol:selCol row:selRow]) // can't add category
  467.         return self ; // to a leaf
  468.     [browser getPath: aPath toColumn: selCol + 1 ] ;     
  469.   }
  470.   else
  471.     aPath[0] = '\0' ;
  472.   sprintf(bigBuf,"cd %s%s ; mkdir %s > /dev/null",rootPath,aPath,newName) ;
  473.   system(bigBuf) ;
  474.   [[browser window] disableFlushWindow] ;
  475.   [browser loadColumnZero] ;
  476.   sprintf(bigBuf,"/%s/%s",aPath,newName) ;
  477.   [browser setPath: bigBuf] ;
  478.   [[browser window] reenableFlushWindow] ;
  479.   [[browser window] flushWindow] ;
  480.   return self ;
  481.   return self ;
  482. }
  483.   
  484. - newClass:sender
  485. { char aPath[512], fileBuf[512], className[128] ;
  486.   int selCol, selRow ;
  487.   strcpy(className,[[name cell] stringValue]) ;
  488.   if(className[0] == '\0') // nothing there !
  489.     return self ; 
  490.   if(!NXIsUpper((unsigned) className[0]))
  491.   { NXRunAlertPanel("Nu","Error: class names must begin capitalized",
  492.      NULL,NULL,NULL) ;
  493.     return self ;
  494.   }
  495.   selCol = [browser selectedColumn] ;
  496.   selRow = [[browser matrixInColumn: selCol] selectedRow] ;
  497.   if([self isDotMCol:selCol row:selRow]) // can't add class
  498.   { NXRunAlertPanel("Nu","Error: select a category, not a class",
  499.        NULL,NULL,NULL) ;
  500.     return self ; // to a leaf
  501.   }
  502.   else
  503.     [browser getPath: aPath toColumn: selCol + 1 ] ;     
  504.   // create the new class's initial text
  505.   sprintf(fileBuf,
  506.     "#import \\\"%s.h\\\"\n"
  507.     "#import \\\"Nutation.h\\\"\n"
  508.     "  // Superclass file goes on next line:\n"
  509.     "#pragma .h #import \\\"Glyph.h\\\"\n"
  510.     "@implementation %s:Glyph\n"
  511.     "{\n"
  512.     "}\n"
  513.     "\n"
  514.     "@end\n",
  515.   className, className) ;  
  516.   // write it to a file
  517.   sprintf(bigBuf,"cd %s%s ; echo \"%s\" > %s.m",
  518.        rootPath,aPath,fileBuf,className) ; 
  519.   system(bigBuf) ; 
  520.   [[browser window] disableFlushWindow] ;
  521.   [browser loadColumnZero] ;
  522.   sprintf(bigBuf,"/%s/%s",aPath,className) ;
  523.   [browser setPath: bigBuf] ;
  524.   [[browser window] reenableFlushWindow] ;
  525.   [[browser window] flushWindow] ;
  526.   return self ;
  527. }
  528.  
  529.  
  530. - rename:sender 
  531. { // NOTE: must rename .o,.h, and .g, if they
  532.   // exist!!!
  533.   char aPath[512], newName[128] ;
  534.   int selCol, selRow ;
  535.   strcpy(newName,[[name cell] stringValue]) ;
  536.   if(newName[0] == '\0') // nothing there !
  537.     return self ; 
  538.   if(NXIsUpper((unsigned) newName[0]))
  539.   { NXRunAlertPanel("Nu","Error: categories must not begin capitalized",
  540.      NULL,NULL,NULL) ;
  541.     return self ;
  542.   }
  543.   if((selCol = [browser selectedColumn]) != -1)
  544.   { selRow = [[browser matrixInColumn: selCol] selectedRow] ;
  545.     if(fileList[0]) // if not empty column
  546.     { if([self isDotMCol:selCol row:selRow]) // can't rename .m 
  547.       { NXRunAlertPanel("Nu","Error: cannot rename a class",
  548.            NULL,NULL,NULL) ;
  549.         return self ; 
  550.       }
  551.     }
  552.     [browser getPath: aPath toColumn: selCol] ;     
  553.   }
  554.   else
  555.     aPath[0] = '\0' ;
  556.   sprintf(bigBuf,"cd %s%s ; mv %s  %s> /dev/null",rootPath,aPath,
  557.      [[[browser matrixInColumn: selCol] selectedCell] stringValue],
  558.      newName) ;
  559.   system(bigBuf) ;
  560.   [[browser window] disableFlushWindow] ;
  561.   [browser loadColumnZero] ;
  562.   sprintf(bigBuf,"/%s/%s",aPath,newName) ;
  563.   [browser setPath: bigBuf] ;
  564.   [[browser window] reenableFlushWindow] ;
  565.   [[browser window] flushWindow] ;
  566.   return self ;
  567. }
  568.   
  569.  
  570. - setBrowser: anObject ;
  571. { browser = anObject ;
  572.   [browser setDelegate: self] ;
  573.   return self ;
  574. }
  575.  
  576.  
  577. - setMsgView: anObject ;
  578. { msgView = anObject ;
  579.   return self ;
  580. }
  581.  
  582. - setup ;
  583. { // initialize myself
  584.   Window *aWin ;
  585.   aWin = [browser window] ;
  586.   [aWin disableFlushWindow] ;
  587.   [browser loadColumnZero] ;
  588.   [browser setPath: "/glyphs"] ;
  589.   [aWin reenableFlushWindow] ;
  590.   [aWin flushWindow] ;
  591.   [glyphView setGlyphMsgView: msgView] ; // set to my msg view
  592.   // this next message must come AFTER setGlyphMsgView:, as
  593.   // setGlyphMsgView: calls GlyphView's setUpNew, which sets the
  594.   // freeWhenClosed flag to YES.  But we don't ever want to free
  595.   // the glyphView in the Glyph Browser:
  596.   [glyphView setFreeWhenClosed: NO] ;  // always keep glyphView around
  597.   return self ;
  598. }
  599.  
  600.  
  601.  
  602. @end
  603.