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