Annotation of Examples/AppKit/Yap/YapDocument.m, revision 1.1.1.1

1.1       root        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 document")
                     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 find Document.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 printInfo] setMarginLeft: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 = documentName ? 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] printPSCode:sender];
                    362:     return self;
                    363: }
                    364: 
                    365: @end

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.