home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.bin / SourceCode / MiscKit1.2.6 / Examples / TreeView / TreeController.m < prev    next >
Encoding:
Text File  |  1994-01-28  |  8.5 KB  |  266 lines

  1.  
  2. #import <stdio.h>
  3.  
  4. #import "TreeController.h"
  5. #import "NamedTree.h"
  6. #import "TreeView.h"
  7.  
  8. @implementation TreeController
  9.  
  10. - appDidInit:(Application *)sender
  11. {
  12.   BOOL haveOpenedDocument = NO;    // whether we have opened a document
  13.  
  14. //  // Gets the public port for SomeApp
  15. //  port_t  thePort = NXPortFromName("Emacs", NULL); 
  16. //
  17. //  if (thePort != PORT_NULL)
  18. //    // Sets the Speaker to send its next message to SomeApp's port
  19. //    [[NXApp appSpeaker] setSendPort:thePort]; 
  20.  
  21.   if (NXArgc > 1)
  22.   {
  23.      int i;
  24.  
  25.      for (i = 1; i < NXArgc; i++)
  26.      {
  27.     haveOpenedDocument = [self openFile: NXArgv[i]] || haveOpenedDocument;
  28.      }
  29.   }
  30.  
  31.     return self;
  32. }
  33.  
  34. - init
  35. {
  36.     [super init];
  37.     first = YES;
  38.     nextX = 200;
  39.     nextY = 600;
  40.       return self;
  41. }
  42.  
  43. - info:sender // bring up the info panel, obviously
  44. {
  45.     if(!infoPanel)
  46.         [NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
  47.     return [infoPanel orderFront:sender];
  48. }
  49.  
  50. - open:sender
  51. {    // use open panel to select a file -- only with .tree extension.
  52.     // only one file may be loaded at a time.
  53.     const char *const *files;
  54.     char *file;
  55.     const char *dir;
  56.     static const char *const ft[2] = {"tree", NULL};
  57.  
  58.     id openPanel = [OpenPanel new];
  59.     [openPanel allowMultipleFiles:NO];
  60.     if (first) {
  61.         [openPanel runModalForDirectory:[[NXBundle mainBundle] directory]
  62.                 file:NULL types:ft];
  63.         first = NO;
  64.     } else  [openPanel runModalForTypes:ft];
  65.     files = [openPanel filenames];
  66.     dir = [openPanel directory];
  67.     file = malloc(strlen(files[0]) + strlen(dir) + 8);
  68.     strcpy(file, dir);
  69.     strcat(file,"/");
  70.     strcat(file, files[0]);
  71.     strcat(file, "\0");
  72.     [self openFile:file];
  73.     return self;
  74. }
  75.  
  76. char nextChar; // This allows me to do single character lookahead in scan
  77.  
  78. id readToNewline(FILE *file) // used to parse a file; reads until a newline
  79. {    // returns a string object... reads until EOL to get string value.
  80.     id newString = [[String alloc] init];
  81.     char *buffer = (char *)malloc(1024);    // should be plenty big
  82.     char *spot = buffer;
  83.  
  84.     while (nextChar != '\n')
  85.       {
  86.         spot[0] = nextChar; spot++; nextChar = fgetc(file);
  87.       }    
  88.     spot[0] = '\0'; // terminate the string
  89.     nextChar = fgetc(file);  // Get next char for next invocation of this function
  90.     [newString setString:buffer];
  91.     free(buffer);
  92.     return newString;
  93. }
  94.  
  95. char prevChar; // This allows me to do single character lookback in scan
  96.  
  97. id readToSep(FILE *file) // used to parse a file; reads until a newline or a "^^" sequence
  98. {    // returns a string object... reads until EOL to get string value.
  99.     id newString = [[String alloc] init];
  100.     char *buffer = (char *)malloc(1024);    // should be plenty big
  101.     char *spot = buffer;
  102.     int c;
  103.  
  104.     while (nextChar != '\n')
  105.       {
  106.         if (nextChar == '^')
  107.           if ((c = fgetc(file)) == '^')
  108.         break;
  109.           else
  110.         ungetc(c, file);
  111.         spot[0] = nextChar; spot++; nextChar = fgetc(file);
  112.       }    
  113.     spot[0] = '\0'; // terminate the string
  114.     prevChar = nextChar;
  115.     nextChar = fgetc(file);  // Get next char for next invocation of this function
  116.     [newString setString:buffer];
  117.     free(buffer);
  118.     return newString;
  119. }
  120.  
  121. // This actually opens a file.  WorkSpace and Open panel methods both
  122. // eventually get to here to do the real work.  This code is pretty much
  123. // worth ignoring, unless you _really_ care about how I read in the
  124. // files.  It's mostly specific to the file format so it's not
  125. // generally useful.  The framework for this code came from the
  126. // code in my "Viewer.app" that is in with some PD raytracers I ported
  127. // to the NeXT--allows viewing an rgb bitmap file; I wrote it before
  128. // GW and ImageViewer existed...  (See raytracers.tar.Z on sonata/orst)
  129. - (BOOL)openFile:(const char *)name
  130. {
  131.     // id alert;
  132.     id aString, treeName, rootNode, workingNode, tempNode;
  133.     id newString, stack = [[List alloc] init];
  134.     int indLevel, numSpaces, indent = 0;
  135.     const char *tempString;
  136.     BOOL rStat = YES;
  137.     FILE *file;
  138.     // for debugging:
  139.     //NXStream *out = NXOpenFile(fileno(stdout), NX_WRITEONLY);
  140.  
  141.     // get a new doc window.
  142.     [NXApp loadNibSection:"DocWindow.nib" owner:self withNames:NO];
  143.     [[treeView window] setTitleAsFilename:name];
  144.     // put up an alert panel to let user know we're busy
  145.     // alert = NXGetAlertPanel(NULL, "Reading tree and creating image.",
  146.     //        NULL, NULL, NULL);
  147.     // [alert makeKeyAndOrderFront:self];
  148.     // Read the tree file.  NOTE THAT THIS DOES NOT DO ERROR CHECKING.
  149.     file = fopen(name, "r");
  150.     nextChar = fgetc(file);    // prime the system
  151.     treeName = readToNewline(file); // first line is tree's name
  152.     aString = readToSep(file);    // Get the name of the root node.
  153.     rootNode = [[[NamedTree alloc]
  154.             initLabelString:aString] setTreeName:treeName];
  155.     if (prevChar != '\n')
  156.       [rootNode setValue: readToSep(file)];  // Set the node's value.
  157.     [stack insertObject:rootNode at:0];
  158.     workingNode = rootNode;
  159.     // figure out the indentation
  160.     while (nextChar == ' ') {
  161.         indent++;
  162.         nextChar = fgetc(file);
  163.     }
  164.     aString = readToSep(file); // get name of child node
  165.     tempNode = [[[NamedTree alloc]
  166.             initLabelString:aString] setTreeName:treeName];
  167.     if (prevChar != '\n')
  168.       [tempNode setValue: readToSep(file)];  // Set the node's value.
  169.     [workingNode addBranch:tempNode];
  170.     [stack insertObject:tempNode at:0];
  171.     workingNode = tempNode;
  172.     // now that we know the file's char's, we read in the other nodes
  173.     // I use a List object as if it were a stack and push parent nodes on
  174.     // it while working on children rather than doing a recursive function.
  175.     // the comments are sparse, just know that it's mostly pushing and
  176.     // popping from the stack to get at the right parent to add a child to.
  177.     while (!feof(file)) {
  178.         aString = readToSep(file); // next node name + indentation.
  179.         // find out # of indentation spaces and strip them off
  180.         // *** This gives a warning: ignore it, it's unimportant here.
  181.         tempString = [aString stringValue]; numSpaces = 0;
  182.         while (tempString[0] == ' ') {
  183.             numSpaces++; tempString++;
  184.         }
  185.         indLevel = numSpaces / indent;
  186.         if (indLevel == ([stack count] - 1)) // same level as last object
  187.           {
  188.             [stack removeObjectAt:0];
  189.             workingNode = [stack objectAt:0];
  190.             newString = [[String alloc] initString:tempString];
  191.             [aString free];
  192.             tempNode = [[[NamedTree alloc]
  193.                     initLabelString:newString] setTreeName:treeName];
  194.                         if (prevChar != '\n')
  195.               [tempNode setValue: readToSep(file)];  // Set the node's value.
  196.             [workingNode addBranch:tempNode];
  197.             [stack insertObject:tempNode at:0];
  198.             workingNode = tempNode;
  199.         } else if (indLevel == ([stack count])) { // child of last node
  200.             newString = [[String alloc] initString:tempString];
  201.             [aString free];
  202.             tempNode = [[[NamedTree alloc]
  203.                     initLabelString:newString] setTreeName:treeName];
  204.                         if (prevChar != '\n')
  205.               [tempNode setValue: readToSep(file)];  // Set the node's value.
  206.             [workingNode addBranch:tempNode];
  207.             [stack insertObject:tempNode at:0];
  208.             workingNode = tempNode;
  209.         } else if (indLevel < [stack count]) { // higher level, so pop
  210.             while (indLevel < [stack count]) { // pop until at right level
  211.                 [stack removeObjectAt:0];
  212.                 workingNode = [stack objectAt:0];
  213.             } // now add new node since we're at the level
  214.             newString = [[String alloc] initString:tempString];
  215.             [aString free];
  216.             tempNode = [[[NamedTree alloc]
  217.                     initLabelString:newString] setTreeName:treeName];
  218.                         if (prevChar != '\n')
  219.               [tempNode setValue: readToSep(file)];  // Set the node's value.
  220.             [workingNode addBranch:tempNode];
  221.             [stack insertObject:tempNode at:0];
  222.             workingNode = tempNode;
  223.         } else { // typically, if user goes in two levels at once, which
  224.             // doesn't make any sense semantically
  225.             fprintf(stderr, "Error: level too deep!\n");
  226.             rStat = NO;
  227.         }
  228.     }
  229.     // Debugging code to pretty print the parsed tree.  If this output
  230.     // is correct, then we know that the parse was OK.
  231.     //printf("\nHere's the parsed tree:\n");
  232.     //printf("Tree name:  \"%s\".", [treeName stringValue]);
  233.     //[rootNode dumpTree:out level:0 indent:"   "];
  234.     //printf("\n\n");
  235.     //NXClose(out);
  236.     // end debug code
  237.     // rStat = return status of tree reader  YES = success
  238.     // Now attach the Tree to the TreeView...
  239.     [treeView attachTree:rootNode];
  240.     // and now bring up the window for the user
  241.     [[treeView window] moveTo:nextX :(nextY-218)];
  242.     // Get rid of the alert
  243.     // [alert orderOut:self];
  244.     // [alert free];
  245.     nextX += 22; nextY -= 25;
  246.     if (nextX > 370)
  247.     {
  248.         nextX = 200; nextY = 600;
  249.     }
  250.     [[treeView window] makeKeyAndOrderFront:self];
  251.     return rStat;
  252. }
  253.  
  254.  
  255. // The next two methods allow the WorkSpace to open a .tree file when
  256. // it is double-clicked.  (Or any file that's cmd-dragged over our icon.)
  257.  
  258. - (BOOL)appAcceptsAnotherFile:sender { return YES; }
  259. - (int)app:sender openFile:(const char *)file type:(const char *)type
  260. {
  261.     return [self openFile:file];
  262. }
  263.  
  264.  
  265. @end
  266.