home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / Yap / YapDocument.m < prev    next >
Encoding:
Text File  |  1992-05-29  |  10.4 KB  |  366 lines

  1. /*
  2.  *  YapDocument.m
  3.  *  Author: Ali Ozer
  4.  *  Created: Aug 28, 1988
  5.  *  Modified for 0.8: Sep 1988
  6.  *  Modified for 0.9 and revised: Feb & Mar 1989
  7.  *  Modified for 1.0 and nibified: Jun & Jul 1989
  8.  *  Modified for 2.0 and zonified: Aug 1990
  9.  *  Modified: Jan 92 for 3.0. Localized.
  10.  *
  11.  *  YapDocument class implements the Yap documents --- for every open 
  12.  *  window, we have another instance of the YapDocument class. Each instance
  13.  *  loads itself into a separate zone.
  14.  *
  15.  *  You may freely copy, distribute and reuse the code in this example.
  16.  *  NeXT disclaims any warranty of any kind, expressed or implied,
  17.  *  as to its fitness for any particular use.
  18.  */
  19.  
  20. #import <appkit/appkit.h>
  21. #import <objc/NXBundle.h>
  22. #import <objc/error.h>
  23. #import <libc.h>
  24. #import <string.h>
  25. #import <sys/file.h>
  26.  
  27. #import "YapDocument.h"
  28. #import "PSText.h"
  29. #import "YapApp.h"
  30. #import "YapOutput.h"
  31. #import "FindPanel.h"
  32.  
  33. #import <objc/NXBundle.h>
  34. #import <objc/List.h>
  35. #import <objc/zone.h>
  36. #import <streams/streams.h>
  37. #import <defaults/defaults.h>
  38. #import <mach/mach.h>
  39. #import <libc.h>
  40. #import <string.h>
  41.  
  42. #define UNTITLED NXLocalString ("UNTITLED", NULL, "Name of default docu!0d")
  43. #define CANTWRITEFILE_STRING NXLocalString ("Can't write file.", NULL, "Document could not be saved")
  44. #define CLOSEWINDOW_STRING NXLocalString("Close", NULL, "Request to close window containing unsaved document from menu or close button.")
  45. #define SAVECHANGES_STRING NXLocalString("%s has changes. Save them?", NULL, "Question asked of user when he/she tries to close a window containing an unsaved document.  The %s is the name of the document.")
  46. #define SAVE_STRING NXLocalString("Save", NULL, "Button choice which allows the user to save the document.")
  47. #define DONTSAVE_STRING NXLocalString("Don't Save", NULL, "Button choice which allows the user to abort the save of a document which is being closed.")
  48. #define OK_STRING NXLocalString ("OK", NULL, "Default response in alert panel")
  49. #define CANCEL_STRING NXLocalString ("Cancel", NULL, "Button choice allowing user to cancel the request to close a window")
  50.  
  51. #define XOFFSET 5.0     // Offset of subsequent windows
  52. #define YOFFSET -20.0
  53. #define MAXSIZE 1.0e38    // Maximum size of a text object
  54.  
  55. @implementation YapDocument
  56.  
  57. /*
  58.  * The next two methods allow us to cache/reuse zones.
  59.  */
  60. static id zoneList = nil;
  61.  
  62. + (NXZone *)newZone
  63. {
  64.     if (!zoneList || ![zoneList count]) {
  65.     return NXCreateZone(vm_page_size, vm_page_size, YES);
  66.     } else {
  67.     return (NXZone *)[zoneList removeLastObject];
  68.     }
  69. }
  70.  
  71. + (void)reuseZone:(NXZone *)aZone
  72. {
  73.     if (!zoneList) zoneList = [List new];
  74.     [zoneList addObject:(id)aZone];
  75. }
  76.  
  77. /*
  78.  * Return the document in the specified window.
  79.  */
  80. + documentForWindow:window
  81. {
  82.     id del = [window delegate];
  83.     return (del && [del isKindOf:[YapDocument class]]) ? del : nil;
  84. }
  85.  
  86. /*
  87.  * Create a new instance of YapDocument with the specified file in the
  88.  * buffer.  If the file cannot be opened, no document is created and nil
  89.  * is returned.
  90.  */
  91. + newFromFile:(const char *)fileName
  92. {
  93.     NXStream *stream = NULL;
  94.     id docWin;        /* Window belonging to this document. */
  95.     id textObj;        /* The text object we put in the window. */
  96.     NXRect textFrame;    /* The frame of the text object in our window */
  97.  
  98.     if (fileName && !(stream = NXMapFile(fileName, NX_READONLY))) {
  99.     return nil;
  100.     }
  101.  
  102.     self = [[self allocFromZone:[self newZone]] init];
  103.     
  104.     if (![NXApp loadNibSection:"Document.nib" owner:self withNames:NO fromZone:[self zone]]) {
  105.     NXLogError ("Can't f!0eDocument.nib!");    
  106.     NXCloseMemory (stream, NX_FREEBUFFER);
  107.         [self free];
  108.     return nil;
  109.     }
  110.  
  111.     /*
  112.      * Loading the nib file above sets the document outlet to the
  113.      * scrollview; so we can use this outlet to get at the window & such.
  114.      */
  115.     docWin = [document window];
  116.     [[document docView] getFrame:&textFrame];
  117.  
  118.     /*
  119.      * Put the window offset from the previous document window... If no
  120.      * previous window exists, or the main window is undetermined, then
  121.      */ 
  122.     if ([NXApp mainWindow]) {
  123.     NXRect winFrame, winLoc;
  124.     [[NXApp mainWindow] getFrame:&winFrame];
  125.     [[docWin class] getContentRect:&winLoc
  126.             forFrameRect:&winFrame
  127.             style:[docWin style]];
  128.     [docWin moveTo:NX_X(&winLoc) + XOFFSET :NX_Y(&winLoc) + YOFFSET];
  129.     }
  130.     
  131.     [self setName:UNTITLED];
  132.     [docWin setDelegate:self];
  133.  
  134.     if (stream) {
  135.     char *text;
  136.     int len, maxLen;
  137.     NXGetMemoryBuffer (stream, &text, &len, &maxLen);
  138.     textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame text:text alignment:NX_LEFTALIGNED];
  139.     [self setName:fileName];
  140.     NXCloseMemory (stream, NX_FREEBUFFER);
  141.     } else {
  142.     textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame];
  143.     [self setName:UNTITLED];
  144.     }
  145.  
  146.     /*
  147.      * Put this new text object in the window and free the IB-created one.
  148.      */
  149.     [[document setDocView:textObj] free];
  150.  
  151.     /*
  152.      * Set various parameters.
  153.      */
  154.     [document setAutoresizeSubviews:YES];
  155.     [textObj setVertResizable:YES];        // Grow down as you type
  156.     [textObj setHorizResizable:NO];        // But not sideways 
  157.     [textObj setAutosizing:NX_WIDTHSIZABLE];    // Size horizontally when resized
  158.     [textObj setMonoFont:YES];
  159.     [textObj setOpaque:YES];
  160.     [textObj setMinSize:&textFrame.size];
  161.     NX_WIDTH(&textFrame) = NX_HEIGHT(&textFrame) = MAXSIZE;
  162.     [textObj setMaxSize:&textFrame.size];    // Can grow
  163.     [textObj setSel:0:0];            // Set the selection
  164.     [textObj setDelegate:self];
  165.     [textObj sizeToFit];
  166.  
  167.     [docWin makeKeyAndOrderFront:self];
  168.  
  169.     [self initializePrintInfo];
  170.  
  171.     return self;
  172. }
  173.  
  174. + new
  175. {
  176.     return [self newFromFile:NULL];
  177. }
  178.  
  179. - initializePrintInfo
  180. {
  181.     static BOOL printInfoInitialized = NO;
  182.     if (!printInfoInitialized) {
  183.     [[NXApp printInfo] setVertCentered:NO];
  184.     [[NXApp printInfo] setHorizCentered:NO];
  185.     [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
  186.     [[NXApp printIn!0fsetMarginLeft:36.0 right:36.0 top:72.0 bottom:72.0];
  187.     printInfoInitialized = YES;
  188.     }
  189.     return self;
  190. }
  191.  
  192. /*
  193.  * Checks to see if the window has been edited...
  194.  */
  195. - (BOOL)needsSaving
  196. {
  197.     return [[document window] isDocEdited];
  198. }
  199.  
  200. /*
  201.  * Delegate method for the document Text object. We use this method
  202.  * to detect when the text in the window is modified.
  203.  */
  204. - text:text isEmpty:(BOOL)empty
  205. {
  206.     if (![[document window] isDocEdited]) {
  207.         [[document window] setDocEdited:YES];
  208.     }
  209.     return NO;
  210. }
  211.  
  212. /*
  213.  * Delegate method for the document Text object. We use this method
  214.  * to detect when the font is changed so we an write it out as the default.
  215.  */
  216. - textWillConvert:textObject fromFont:oldFont toFont:newFont
  217. {
  218.     if (newFont) {
  219.         char str[80];
  220.     sprintf (str, "%f", [newFont pointSize]);
  221.     NXWriteDefault ([NXApp appName], "NXFontSize", str);
  222.     NXWriteDefault ([NXApp appName], "NXFont", [newFont name]);
  223.     [Text setDefaultFont:newFont];
  224.     }
  225.  
  226.     return newFont;
  227. }
  228.  
  229. /*
  230.  * saveDocument: will write out the contents of the document
  231.  * to the specified file. 
  232.  * 
  233.  * If fileName is NULL, asks user for a file name.
  234.  * Returns NO if the user decides to cancel the operation.
  235.  * Otherwise returns YES (whether or not the document could be saved),
  236.  * modifying the needsSaving state.
  237.  */
  238. - (BOOL)saveDocument:(const char *)fileName
  239. {
  240.     int fd;     // File descriptor
  241.     NXStream *stream = NULL;
  242.     BOOL saveOK;
  243.  
  244.     if (!fileName || !strcmp (fileName, UNTITLED) || !strcmp (fileName, "")) {
  245.     if (!(fileName = [[SavePanel new] runModalForDirectory:"." file:UNTITLED] ? [[SavePanel new] filename] : NULL)) {
  246.         return NO;
  247.     }
  248.     }
  249.  
  250.     if (saveOK = 
  251.         (((fd = open (fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644)) != -1) &&
  252.         (stream = NXOpenFile (fd, NX_WRITEONLY))))
  253.     [[document docView] writeText:stream];
  254.     if (stream) NXClose (stream);
  255.     if (fd != -1) close (fd);
  256.  
  257.     if (saveOK) {
  258.     [self setName:fileName];
  259.     [[document window] setDocEdited:NO];
  260.     } else {
  261.     NXRunAlertPanel (NULL, CANTWRITEFILE_STRING, OK_STRING, NULL, NULL);
  262.     }
  263.     return YES;
  264. }
  265.  
  266. /*
  267.  * Set/Get the name of the document. This is the same as the title.
  268.  * Free the old name after the new one is set, because sometimes this
  269.  * routine might be called as the old name as the argument...
  270.  */
  271. - setName:(const char *)documentName
  272. {
  273.     documentName = doc!0gtName ? documentName : "";
  274.     if (documentName != name) {
  275.     free(name);
  276.     name = NXCopyStringBufferFromZone (documentName, [self zone]); 
  277.     [[document window] setTitleAsFilename:name];
  278.     }
  279.     return self;
  280. }
  281.  
  282. -(const char *)name
  283. {
  284.     return name;
  285. }
  286.  
  287. /*
  288.  * windowWillClose: gets called by windows who have this instance of 
  289.  * YapDocument as delegate.  We call closeDocument:andWindow: to see if the
  290.  * document needs saving and take the appropriate action if so. If the user
  291.  * cancels the save, closeDocument:andWindow: returns NO and we return nil.
  292.  * This prevents the window from closing...
  293.  */
  294. - windowWillClose:sender
  295. {
  296.     return [self closeDocument:CLOSEWINDOW_STRING andWindow:NO] ? self : nil;
  297. }
  298.  
  299. /*
  300.  * Closes the document. If document needs saving, asks user he/she'd like the doc
  301.  * saved. Returns NO if the user cancels out of the save operation, otherwise returns YES.
  302.  * flag determines if the window should also be closed with the document.
  303.  */
  304. - (BOOL)closeDocument:(const char *)message andWindow:(BOOL)flag
  305. {
  306.     if ([self needsSaving]) {
  307.     int save = NXRunAlertPanel(message, SAVECHANGES_STRING, SAVE_STRING, DONTSAVE_STRING, CANCEL_STRING, [self name]);
  308.     if (save == NX_ALERTOTHER) {    // Cancel
  309.         return NO;
  310.     } else if (save == NX_ALERTDEFAULT) {
  311.         [self save:nil];
  312.     }
  313.     }
  314.     [[document window] setDelegate:nil];
  315.     if (flag) [[document window] close];
  316.     [self free];
  317.     return YES;
  318. }
  319.  
  320. - free
  321. {
  322.     NXZone *docZone = [self zone];
  323.     if (name) free(name);
  324.     [super free];
  325.     [YapDocument reuseZone:docZone];
  326.     return nil;
  327. }
  328.     
  329. /*
  330.  * save: saves the current document. If the document is untitled, it 
  331.  * puts up a savePanel to get the user to enter a file name. saveAs:
  332.  * saves the document under a new name by putting up a savePanel.
  333.  */
  334. - save:sender 
  335. {
  336.     (void)[self saveDocument:[self name]];
  337.     return self;
  338. }
  339.   
  340. - saveAs:sender 
  341. {
  342.     if ([[SavePanel new] runModalForDirectory:"." file:[self name]]) {
  343.         (void)[self saveDocument:[[SavePanel new] filename]];
  344.     }
  345.     return self;
  346. }
  347.  
  348. - execute:sender
  349. {
  350.     [[NXApp outputView] executeCodeFrom:[document docView]];
  351.  
  352.     return self;
  353. }   
  354.  
  355. /*
  356.  * To get around the problem of printPSCode: going up the responder chain and
  357.  * causing print panel to come back after Cancel, we use the following glue.
  358.  */
  359. - print:sender
  360. {
  361.     [[document docView] print!0hde:sender];
  362.     return self;
  363. }
  364.  
  365. @end
  366.