home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / Yap / YapOutput.m < prev    next >
Encoding:
Text File  |  1992-02-10  |  8.5 KB  |  282 lines

  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 !0qe 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.         DPSPrintErrorToStr!0r(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[STATUSLENG!0s    /* 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