|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.