|
|
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.