|
|
1.1 root 1: /*
2: * YapOutput.m
3: * Author: Ali Ozer
4: * Created: Mar 6, 1989
5: * Modified: Jun & Jul 1989 for 1.0. Added NX_HANDLER for error detection.
6: * Modified: Aug 90 for 2.0. Added use of second context for robustness.
7: * Modified: Jan 92 for 3.0. Localized.
8: *
9: * This class is a subclass of view that manages the output in Yap. It
10: * provides a method (executeCodeFrom:) to execute PS from a text object.
11: * The PostScript output will be displayed in the view. The output is
12: * also cached in a bitmap for fast redraw response.
13: *
14: * You may freely copy, distribute and reuse the code in this example.
15: * NeXT disclaims any warranty of any kind, expressed or implied,
16: * as to its fitness for any particular use.
17: */
18:
19: #import <appkit/appkit.h>
20: #import <objc/NXBundle.h>
21: #import <objc/error.h>
22: #import <libc.h>
23: #import <string.h>
24: #import <sys/file.h>
25:
26: #import "YapOutput.h"
27: #import "YapApp.h"
28: #import "YapWrap.h"
29:
30: #define BUSY_STRING NXLocalString ("BUSY", NULL, "String shown when PostScript is being executed")
31: #define EXECUTION_COMPLETE_STRING NXLocalString("Yap Postscript Output (Execution Time %d ms)", NULL, "Shown when PostScript has executed successfully")
32: #define NONPOSTSCRIPT_ERROR_STRING NXLocalString("A non-PostScript error while running program.", NULL, "Shown if a non-PostScript error occurs during execution")
33: #define NOCONTEXT_STRING NXLocalString("Could not connect to window server.", NULL, "Shown if a connection cannot be created with the window server")
34:
35: @implementation YapOutput
36:
37: /*
38: * initFrame: initializes the instance and creates a cache.
39: */
40: - initFrame:(const NXRect *)viewFrame
41: {
42: [super initFrame:viewFrame];
43: [self setCacheCleared:YES];
44: [self setCacheShown:NO];
45: cache = [[Window allocFromZone:[self zone]]
46: initContent:&bounds
47: style:NX_PLAINSTYLE
48: backing:NX_RETAINED
49: buttonMask:0
50: defer:NO];
51:
52: return self;
53: }
54:
55: /*
56: * Set/get parameters that determine behaviour.
57: */
58: - (BOOL)isCacheCleared
59: {
60: return clearCache;
61: }
62:
63: - (BOOL)isCacheShown
64: {
65: return showCache;
66: }
67:
68: - setCacheCleared:(BOOL)flag
69: {
70: clearCache = flag;
71: return self;
72: }
73:
74: - setCacheShown:(BOOL)flag
75: {
76: showCache = flag;
77: return self;
78: }
79:
80: /*
81: * sizeTo:: is called whenever the view is resized. It resizes the bitmap cache
82: * along with the view. It doesn't do anything if the new size is equal to the
83: * old one.
84: */
85: - sizeTo:(NXCoord)width :(NXCoord)height
86: {
87: if (width == frame.size.width && height == frame.size.height) return self;
88:
89: [super sizeTo:width :height];
90: [cache sizeWindow:width :height];
91:
92: return self;
93: }
94:
95: /*
96: * drawSelf: simply shows the cache.
97: */
98: - drawSelf:(NXRect *)rects :(int)rectCount
99: {
100: if (rectCount == 3) { /* Scrolling diagonally; use last two rectangles */
101: rects++;
102: PScomposite (NX_X(rects), NX_Y(rects), NX_WIDTH(rects), NX_HEIGHT(rects),
103: [cache gState], NX_X(rects), NX_Y(rects), NX_COPY);
104: rects++;
105: PScomposite (NX_X(rects), NX_Y(rects), NX_WIDTH(rects), NX_HEIGHT(rects),
106: [cache gState], NX_X(rects), NX_Y(rects), NX_COPY);
107: } else {
108: PScomposite (NX_X(rects), NX_Y(rects), NX_WIDTH(rects), NX_HEIGHT(rects),
109: [cache gState], NX_X(rects), NX_Y(rects), NX_COPY);
110: }
111:
112: return self;
113: }
114:
115: - free
116: {
117: [cache free];
118: return [super free];
119: }
120:
121: /*
122: * Ugly function to write PostScript error.
123: */
124: void WritePostScriptError (char *errStr, int errStrLength, NXHandler *errorState)
125: {
126: char *streamAddr;
127: int streamLength, maxLength;
128: NXStream *errorStream;
129:
130: if ((errorState->code == dps_err_ps) &&
131: (errorStream = NXOpenMemory (NULL, 0, NX_WRITEONLY))) {
132: DPSPrintErrorToStream (errorStream, (DPSBinObjSeq)(errorState->data2));
133: NXFlush (errorStream);
134: NXGetMemoryBuffer (errorStream, &streamAddr, &streamLength, &maxLength);
135: if (streamLength > errStrLength-1) streamLength = errStrLength-1;
136: strncpy (errStr, streamAddr, streamLength);
137: errStr[streamLength] = 0;
138: NXCloseMemory (errorStream, NX_FREEBUFFER);
139: } else {
140: sprintf (errStr, NONPOSTSCRIPT_ERROR_STRING);
141: }
142: }
143:
144: /*
145: * SwitchContextsWithFocus() will make the specified context the current
146: * context and make it focus on the same area the old context was
147: * focused on.
148: */
149: static void SwitchContextsWithFocus (DPSContext newContext)
150: {
151: float c1x, c1y, c2x, c2y;
152: float winCTM[6];
153: int realWinNum;
154:
155: GetFocus (&c1x, &c1y, &c2x, &c2y, winCTM, &realWinNum);
156: DPSSetContext (newContext);
157: ReFocus (realWinNum, winCTM, c1x, c1y, c2x, c2y);
158: }
159:
160: #define STATUSLENGTH 200 // Some large number for error string length
161:
162: /*
163: * executeCodeFrom: treats the contents of the specified text object as
164: * a PostScript program and executes it. The code is copied from the
165: * text object into a memory stream and then sent to the server using
166: * DPSWriteData().
167: *
168: * For protection against errors, the PostScript code is interpreted in a
169: * context separate from the Application's own context (which is created
170: * in the +new method of Application). We first focus on the cache,
171: * note the various parameters (global window number, the transformation
172: * matrix, and the clip path), and then switch to the alternate context and
173: * reapply the parameters to establish a focus on the same area.
174: *
175: * Protection against PostScript errors is provided through the use of
176: * NX_DURING/NX_HANDLER. If an error occurs, we immediately blast the
177: * second context and report the first error encountered. If no errors
178: * occur during the execution, then we hang on to the context as it can
179: * be reused.
180: *
181: * Note that the NXEPSImageRep class provides a similar (but more powerful)
182: * sort of functionality for EPS files. Use that class rather than the code
183: * here if you wish to make use of EPS files in your application. This code
184: * here is meant for unstructured, short pieces of PostScript code,
185: * eactly the kind that Yap encounters...
186: */
187: - executeCodeFrom:textObj
188: {
189: int utime; /* Time taken to execute the code */
190: char status[STATUSLENGTH]; /* Array for error messages */
191: NXStream *psStream; /* Memory stream for the PostScript */
192: char *psBuffer; /* The buffer used by the stream */
193: int psLen; /* And the length of this buffer */
194: static DPSContext yapContext = NULL; /* The second context */
195: DPSContext curContext = DPSGetCurrentContext();
196: NXHandler exception;
197:
198: /* Open a memory stream and write the text into it... */
199:
200: if (psStream = NXOpenMemory (NULL, 0, NX_WRITEONLY)) {
201: int dummy;
202: NXPrintf (psStream, "/yaptime usertime def /yapsave save def "
203: "/yapwidth %f def /yapheight %f def "
204: "/showpage {} def\n",
205: bounds.size.width, bounds.size.height);
206: [textObj writeText:psStream];
207: NXPrintf (psStream, "\nyapsave restore "
208: "/yaptime usertime yaptime sub def\n");
209: NXFlush (psStream);
210: NXGetMemoryBuffer (psStream, &psBuffer, &psLen, &dummy);
211: } else {
212: [[self window] setTitle:NONPOSTSCRIPT_ERROR_STRING];
213: return self;
214: }
215:
216: [[self window] setTitle:BUSY_STRING];
217:
218: /* Lock focus on the cache. If user wishes to see the cache, bring it up. */
219:
220: [[cache contentView] lockFocus];
221: if (clearCache) NXEraseRect (&bounds);
222: if (showCache) [[cache center] orderFront:self];
223:
224: /* Create the second context if it needs to be created. */
225:
226: if (yapContext == NULL) {
227: const char *app = [NXApp appName];
228: yapContext = DPSCreateNonsecureContext(
229: NXGetDefaultValue(app, "NXHost"),
230: NXGetDefaultValue(app, "NXPSName"),
231: NULL, NULL, 60000, [self zone]);
232: DPSSetContext (curContext);
233: }
234:
235: if (yapContext) {
236:
237: /* Focus the second context to whatever the first context is focused on. */
238:
239: SwitchContextsWithFocus (yapContext);
240:
241: /* This will let us know if there were any errors. */
242:
243: exception.code = 0;
244:
245: NX_DURING {
246: DPSWriteData (yapContext, psBuffer, psLen);
247: NXPing (); /* This does not return until the execution is done. */
248: /* If there were any errors, we jump to the handler. */
249: GetUserTime (&utime);
250: sprintf (status, EXECUTION_COMPLETE_STRING, utime);
251: } NX_HANDLER {
252: exception = NXLocalHandler; /* Make note of the error... */
253: } NX_ENDHANDLER
254:
255: /* Switch back to the app context. */
256:
257: DPSSetContext(curContext);
258:
259: /* In case of error, report it and blow the second context away. */
260:
261: if (exception.code != 0) {
262: DPSDestroyContext(yapContext);
263: yapContext = NULL;
264: WritePostScriptError (status, STATUSLENGTH, &exception);
265: }
266:
267: } else {
268: sprintf (status, NOCONTEXT_STRING, utime);
269: }
270:
271: if (showCache) [cache orderOut:self];
272: [[cache contentView] unlockFocus];
273: [self display];
274: [[self window] setTitle:status];
275:
276: NXCloseMemory (psStream, NX_FREEBUFFER);
277:
278: return self;
279: }
280:
281:
282: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.