home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 (1993) / nebula.bin / SourceCode / MiniExamples / WhatsUpDoc / Document.m < prev    next >
Encoding:
Text File  |  1991-08-02  |  7.5 KB  |  346 lines

  1. /* Document.m
  2.  * Purpose:   Initializes, loads, archives and frees a single
  3.  *    document.  The template for the document is located in
  4.  *    Document.nib, which is loaded by this class each time
  5.  *    a new document is created.  The windows are tiled using
  6.  *    the newLocation() function.
  7.  *
  8.  * You may freely copy, distribute, and reuse the code in this example.
  9.  * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  10.  * fitness for any particular use.
  11.  *
  12.  * Written by: R. Dunbar Poor
  13.  * Created: 28/April/1991
  14.  *
  15.  */
  16. #import "Document.h"
  17. #import "DocController.h"
  18. #import <appkit/Application.h>
  19. #import <appkit/Cell.h>
  20. #import <appkit/Panel.h>    /* for NX_ALERTxxx */
  21. #import <appkit/SavePanel.h>
  22. #import <appkit/Window.h>    /* for setTitleAsFilename */
  23. #import <objc/hashtable.h>    /* for NXCopyStringBufferFromZone */
  24. #import <sys/param.h>
  25. #import <strings.h>        /* for strncpy() */
  26. #import <stdlib.h>
  27.  
  28. @interface Document(DocumentPrivate)
  29. - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain;
  30. - _write:(const char *)filename;
  31. - _read:(const char *)filename;
  32. @end
  33.  
  34. static void newLocation(NXPoint *p)
  35. /*
  36.  * This method computes a new location for each new window created.
  37.  */
  38. {
  39.   static count = 0;
  40.   p->x += (20.0 * count);
  41.   p->y -= (25.0 * count);
  42.   count = (count > 10)? 0 : count+1;
  43. }
  44.  
  45.  
  46. @implementation Document
  47.  
  48. - init
  49. /*
  50.  * The default initialization is simply to open a new (empty) document.
  51.  */
  52. {
  53.   return [self initFromFile:NULL];
  54. }
  55.  
  56. - initFromFile:(const char *)filename
  57. /*
  58.  * Read a document and initialize it from a file.
  59.  */
  60. {
  61.   [super init];
  62.   [NXApp loadNibSection:"Document.nib" owner:self withNames:NO];
  63.   [self loadFromFile:filename];
  64.   if (!filename) {
  65.     NXRect theFrame;
  66.     /* If its a new document, generate a new position for it */
  67.     [docWindow getFrame:&theFrame];
  68.     newLocation(&theFrame.origin);
  69.     [docWindow moveTo:theFrame.origin.x :theFrame.origin.y];
  70.   }
  71.   [docWindow makeKeyAndOrderFront:self];
  72.   /*
  73.    * I'm not sure why windowDidBecomeMain isn't called as a result of
  74.    * makeKeyAndOrderFront.  At any rate, we want new windows to become
  75.    * the "active" document.
  76.    */
  77.   [self windowDidBecomeMain:self];
  78.   return self;
  79. }
  80.  
  81. - loadFromFile:(const char *)filename
  82. /*
  83.  * Come here with all the nib objects instantiated.  We initialize any
  84.  * application-specific state from the contents from the given filename,
  85.  * and finish up any initialization.
  86.  */
  87. {
  88.   if (filename) {
  89.     if (![self _read:filename]) {
  90.       [self free];    /* couldn't load document file */
  91.       return nil;
  92.     }
  93.   }
  94.  
  95.   /*
  96.    * do some common setup.
  97.    */
  98.   [self setDocumentName:filename];
  99.   [self setDocEdited:NO];
  100.  
  101.   return self;
  102. }
  103.  
  104. - free
  105. {
  106.   /* tell the controller that we can no longer be the active doucment */
  107.   [[NXApp delegate] unsetActiveDocument:self];
  108.   if (name) free(name);
  109.   [docWindow free];
  110.   return [super free];
  111. }
  112.  
  113. - (const char *)message
  114. {
  115.   return [docContents stringValue];
  116. }
  117.  
  118. - activateDocument:sender
  119. {
  120.   [statusField setStringValue:"Active..."];
  121.   return self;
  122. }
  123.  
  124. - deactivateDocument:sender
  125. {
  126.   [statusField setStringValue:"Inactive..."];
  127.   return self;
  128. }
  129.  
  130. - hideDocument:sender
  131. {
  132.   [docWindow orderOut:sender];
  133.   return self;
  134. }
  135.  
  136. - setDocEdited:(BOOL)edited
  137. {
  138.   [docWindow setDocEdited:edited];
  139.   /*
  140.    * The following is a hack to tell the controller to update the
  141.    * Revert menu cell.  Unfortunately, it does lots of work besides
  142.    * that...
  143.    */
  144.   if ([[NXApp delegate] activeDocument] == self) {
  145.     [[NXApp delegate] setActiveDocument:self];
  146.   }
  147.   return self;
  148. }
  149.  
  150. - (BOOL)isDocEdited
  151. {
  152.   return [docWindow isDocEdited];
  153. }
  154.  
  155. - setDocumentName:(const char *)newName
  156. {
  157.   /*
  158.    * If we are passing 'name' itself as  the newName argument, as will happen
  159.    * in the revert: method, we don't want to modify it.  In particular, we
  160.    * better not call free() on it and then try to copy it back to itself (which
  161.    * was a bug I had for a while.)
  162.    */
  163.   if (newName != name) {
  164.     /* name isn't the same as newName, so it is safe to free name now */
  165.     if (name) free(name);
  166.     if (newName) {
  167.       name = NXCopyStringBuffer(newName);
  168.     } else {
  169.       name = NULL;
  170.     }
  171.   }
  172.  
  173.   if (name) {
  174.     [docWindow setTitleAsFilename:name];
  175.   } else {
  176.     [docWindow setTitleAsFilename:"Untitled Document"];
  177.   }
  178.  
  179.   return self;
  180. }
  181.  
  182. - save:sender
  183. {
  184.   if ([self isDocEdited] || !name) {
  185.     [self _saveWithNewName:NO retainNewName:YES];
  186.   }
  187.   return self;
  188. }
  189. - saveAs:sender {  return [self _saveWithNewName:YES retainNewName:YES]; }
  190. - saveTo:sender {  return [self _saveWithNewName:YES retainNewName:NO];  }
  191.  
  192. - revert:sender
  193. {
  194.   int choice;
  195.  
  196.   if ([self isDocEdited]) {
  197.     choice = NXRunAlertPanel(
  198.         "Revert",
  199.         "Discard changes to the document?",
  200.         "Revert",
  201.         "Cancel",
  202.         NULL);
  203.     switch (choice) {
  204.       case NX_ALERTDEFAULT:
  205.     [self loadFromFile:name];
  206.     break;
  207.       case NX_ALERTOTHER:
  208.     return nil;
  209.     }
  210.   }
  211.   return self;
  212. }
  213.  
  214. - close:sender
  215. {
  216.   [docWindow performClose:self];
  217.   return sender;
  218. }
  219.  
  220. - dirty:sender
  221. {
  222.   [self setDocEdited:YES];
  223.   return [self windowDidBecomeMain:self];
  224. }
  225.  
  226. - checkForEdited:sender
  227. /*
  228.  * If the document is edited, give the user a chance to save the
  229.  * document.  Returns nil if they want to cancel.
  230.  */
  231. {
  232.   int choice;
  233.  
  234.   if ([self isDocEdited]) {
  235.     [docWindow makeKeyAndOrderFront:self];
  236.     choice = NXRunAlertPanel(
  237.         "Close",
  238.         "Save changes to %s?",
  239.         "Save",        /* NX_ALERTDEFAULT */
  240.         "Don't Save",    /* NX_ALERTALTERNATE */
  241.         "Cancel",    /* NX_ALERTOTHER */
  242.         (name)?name:"Untitled Document");
  243.     switch (choice) {
  244.       case NX_ALERTALTERNATE:
  245.     break;
  246.       case NX_ALERTDEFAULT:
  247.     [self save:nil];
  248.     break;
  249.       case NX_ALERTOTHER:
  250.     return nil;
  251.     }
  252.   }
  253.   return self;
  254. }
  255.  
  256. - windowDidBecomeMain:sender
  257. {
  258.   [[NXApp delegate] setActiveDocument:self];
  259.   return [self activateDocument:sender];
  260. }
  261.  
  262. - windowDidResignMain:sender
  263. {
  264.   return [self deactivateDocument:sender];
  265. }
  266.  
  267. - windowWillClose:sender
  268. {
  269.   if (![self checkForEdited:sender]) return nil;
  270.  
  271.   /* make the document disavow any knowledge of the window */
  272.   [docWindow setDelegate:nil];
  273.   docWindow = nil;
  274.  
  275.   /* The document can't live now that its window is gone... */
  276.   [self free];
  277.   return sender;
  278. }
  279.  
  280. /*
  281.  * All varieties of save go through this routine.  It covers all the cases
  282.  * of running the Save Panel and retaining the name chosen.
  283.  */
  284. - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain
  285. {
  286.   id savePanel;
  287.   const char *saveName;        /* filename to save into */
  288.  
  289.   if (!name || doNewName) {
  290.     /* saveAs or saveTo */
  291.     savePanel = [SavePanel new];
  292.     [savePanel setRequiredFileType:DOCUMENT_TYPE];
  293.     if ([savePanel runModalForDirectory:NULL file:name]) {
  294.       saveName = [savePanel filename];
  295.     } else {
  296.       /* aborted out? */
  297.       return self;
  298.     }
  299.   } else {
  300.     /* ordinary Save */
  301.     saveName = name;
  302.   }
  303.   [self _write:saveName];
  304.  
  305.   /* update the document name if requested */
  306.   if (doRetain) {
  307.     [self setDocumentName:saveName];
  308.     [docWindow setDocEdited:NO];
  309.   }
  310.   return self;
  311. }
  312.  
  313. - _write:(const char *)filename
  314. {
  315.   NXTypedStream *ts;
  316.   NXRect theFrame;
  317.   const char *contents;
  318.  
  319.   ts = NXOpenTypedStreamForFile(filename, NX_WRITEONLY);
  320.   [docWindow getFrame:&theFrame];
  321.   NXWriteRect(ts, &theFrame);
  322.   contents = [docContents stringValue];
  323.   NXWriteType(ts, "*", &contents);
  324.   NXCloseTypedStream(ts);
  325.  
  326.   return self;
  327. }
  328.  
  329. - _read:(const char *)filename
  330. {
  331.   NXTypedStream *ts;
  332.   NXRect theFrame;
  333.   char *contents;
  334.  
  335.   ts = NXOpenTypedStreamForFile(filename, NX_READONLY);
  336.   NXReadRect(ts, &theFrame);
  337.   [docWindow placeWindowAndDisplay:&theFrame];
  338.   NXReadType(ts, "*", &contents);
  339.   [docContents setStringValue:contents];
  340.   NXCloseTypedStream(ts);
  341.  
  342.   return self;
  343. }
  344.  
  345. @end
  346.