home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Examples / TreeView / TreeController.m < prev    next >
Encoding:
Text File  |  1995-12-20  |  10.4 KB  |  333 lines

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