This is YapOutput.m in view mode; [Download] [Up]
/* * YapOutput.m * Author: Ali Ozer * Created: Mar 6, 1989 * Modified: Jun & Jul 1989 for 1.0. Added NX_HANDLER for error detection. * Modified: Aug 90 for 2.0. Added use of second context for robustness. * Modified: Jan 92 for BBFig by Izumi Ohzawa (izumi@pinoko.berkeley.edu) * * This class is a subclass of view that manages the output in Yap. It * provides a method (executeCodeFrom:) to execute PS from a text object. * The PostScript output will be displayed in the view. The output is * also cached in a bitmap for fast redraw response. * * You may freely copy, distribute and reuse the code in this example. * NeXT disclaims any warranty of any kind, expressed or implied, * as to its fitness for any particular use. */ /* # A portion of code in this file has been taken from NX_HitDetect example # as obtained from the ps-file-server@adobe.com. # The code has been heavily modified and merged with the Yap code as above. # The following sections (a, d, e, and f) are included to keep Adobe happy. # [Izumi Ohzawa] # * (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved. * * (d) This file may be freely copied and redistributed as long as: * 1) Parts (a), (d), (e) and (f) continue to be included in the file, * 2) If the file has been modified in any way, a notice of such * modification is conspicuously indicated. * * (e) PostScript, Display PostScript, and Adobe are registered trademarks of * Adobe Systems Incorporated. * * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO * CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED * AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED. * ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY * OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO * WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY) * WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY * DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. */ #import <appkit/appkit.h> #import <objc/error.h> #import <libc.h> #import <string.h> #import <sys/file.h> #import <stdio.h> #import "YapOutput.h" #import "YapDocument.h" #import "YapApp.h" #import "YapWrap.h" // Generated from YapWrap.psw #import "BBox.h" // Generated from BBox.psw /* This section of code for specifying BBox my dragging a box by mouse. * Stolen from Draw.app's GraphicView.m * */ /* Code-cleaning macros */ #define stopTimer(timer) if (timer) { \ NXEndTimer(timer); \ timer = NULL; \ } #define startTimer(timer) if (!timer) timer = NXBeginTimer(NULL, 0.1, 0.1); /* * Returns the rectangle which has p1 and p2 as its corners. */ static void getRegion(NXRect *region, const NXPoint *p1, const NXPoint *p2) { region->size.width = p1->x - p2->x; region->size.height = p1->y - p2->y; if (region->size.width < 0.0) { region->origin.x = p2->x + region->size.width; region->size.width = ABS(region->size.width); } else { region->origin.x = p2->x; } if (region->size.height < 0.0) { region->origin.y = p2->y + region->size.height; region->size.height = ABS(region->size.height); } else { region->origin.y = p2->y; } } @implementation YapOutput /* * initFrame: initializes the instance and creates a cache. */ - initFrame:(const NXRect *)viewFrame { [super initFrame:viewFrame]; [self setCacheCleared:YES]; [self setCacheShown:NO]; [self setMeshON:NO]; [self setBlueON:NO]; [self setFigureBB:NO]; blackBG = YES; currentDocument = nil; rubberBandColor = NX_COLORRED; cache = [[Window allocFromZone:[self zone]] initContent:&bounds style:NX_PLAINSTYLE backing:NX_RETAINED buttonMask:0 defer:NO]; return self; } - setRubberBandColor:(NXColor)color { rubberBandColor = color; return self; } - (BOOL)acceptsFirstResponder { return YES; } /* This method handles a mouse down. */ - mouseDown:(NXEvent *)event { NXPoint p; int oldMask; // [window makeFirstResponder:self]; /* give me the first responder status */ p = event->location; [self convertPoint:&p fromView:nil]; /* to this view's coordinate from window's */ oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK]; [self dragSelect:event]; // [window flushWindow]; [window setEventMask:oldMask]; return self; } #define DRAG_MASK (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK|NX_TIMERMASK) /* * Allows the user the drag out a box to select all objects either * intersecting the box, or fully contained within the box (depending * on the state of the ALTERNATE key). After the selection is made, * the slist is updated. */ - dragSelect:(NXEvent *)event { NXPoint p, last, start; NXTrackingTimer *timer = NULL; NXRect visibleRect, oldRegion; BOOL canScroll, oldRegionSet = NO; BOOL didDrag = NO; p = start = event->location; [self convertPoint:&start fromView:nil]; last = start; [self lockFocus]; [self getVisibleRect:&visibleRect]; NXSetRect(®ion, 0.0, 0.0, 0.0, 0.0); // drag happend, reset current rubberband region [self drawSelf:&visibleRect :1]; // clear old rubber band rectangle canScroll = !NXEqualRect(&visibleRect, &bounds); if (canScroll) startTimer(timer); PSsetlinewidth(0.0); event = [NXApp getNextEvent:DRAG_MASK]; while (event->type != NX_MOUSEUP) { if (event->type == NX_TIMER) event->location = p; p = event->location; [self convertPoint:&p fromView:nil]; if(!didDrag && event->type == NX_MOUSEDRAGGED) didDrag = YES; if(p.x < 0.0) // bounds.origin.x ? p.x = 0.0; else if(p.x > (bounds.origin.x+bounds.size.width)) p.x = bounds.origin.x+bounds.size.width; if(p.y < 0.0) p.y = 0.0; else if(p.y > (bounds.origin.y+bounds.size.height)) p.y = bounds.origin.y+bounds.size.height; if (p.x != last.x || p.y != last.y) { getRegion(®ion, &p, &start); [window disableFlushWindow]; if (oldRegionSet) { NXInsetRect(&oldRegion, -1.0, -1.0); [self drawSelf:&oldRegion :1]; } if (canScroll) { [self scrollRectToVisible:®ion]; [self scrollPointToVisible:&p]; } NXSetColor(rubberBandColor); PSrectstroke(region.origin.x, region.origin.y, region.size.width, region.size.height); [[window reenableFlushWindow] flushWindow]; oldRegion = region; oldRegionSet = YES; last = p; NXPing(); } p = event->location; event = [NXApp getNextEvent:DRAG_MASK]; } if (canScroll) stopTimer(timer); [self drawSelf:&visibleRect :1]; [self unlockFocus]; // Report the user-specified BBox to the document's BBox field if(currentDocument && didDrag) [currentDocument setBBoxField: region.origin.x :region.origin.y :region.origin.x+region.size.width :region.origin.y+region.size.height]; return self; } - scrollPointToVisible:(const NXPoint *)point { NXRect r; r.origin.x = point->x - 5.0; r.origin.y = point->y - 5.0; r.size.width = r.size.height = 10.0; return [self scrollRectToVisible:&r]; } /* * Set/get parameters that determine behaviour. */ - (BOOL)isCacheCleared { return clearCache; } - (BOOL)isCacheShown { return showCache; } - (BOOL)isMeshON { return meshON; } - (BOOL)isBlueON { return blueON; } - (BOOL)isFigureBB { return figureBB; } - setCacheCleared:(BOOL)flag { clearCache = flag; return self; } - setCacheShown:(BOOL)flag { showCache = flag; return self; } - setMeshON:(BOOL)flag { meshON = flag; return self; } - setBlueON:(BOOL)flag { blueON = flag; return self; } - setFigureBB:(BOOL)flag { figureBB = flag; return self; } /* * sizeTo:: is called whenever the view is resized. It resizes the bitmap cache * along with the view. It doesn't do anything if the new size is equal to the * old one. */ - sizeTo:(NXCoord)width :(NXCoord)height { if (width == frame.size.width && height == frame.size.height) return self; [super sizeTo:width :height]; [cache sizeWindow:width :height]; return self; } /* * drawSelf: simply shows the cache. */ - drawSelf:(NXRect *)rects :(int)rectCount { /* if rectCount == 3, Scrolling diagonally; use last two rectangles */ /* if (rectCount == 3) { rects++; PScomposite (NX_X(rects), NX_Y(rects), NX_WIDTH(rects), NX_HEIGHT(rects), [cache gState], NX_X(rects), NX_Y(rects), NX_COPY); rects++; PScomposite (NX_X(rects), NX_Y(rects), NX_WIDTH(rects), NX_HEIGHT(rects), [cache gState], NX_X(rects), NX_Y(rects), NX_COPY); } else { */ PScomposite (NX_X(rects), NX_Y(rects), NX_WIDTH(rects), NX_HEIGHT(rects), [cache gState], NX_X(rects), NX_Y(rects), NX_COPY); /* } */ NXSetColor(rubberBandColor); PSsetlinewidth(0.0); PSrectstroke(region.origin.x, region.origin.y, region.size.width, region.size.height); return self; } - free { [cache free]; return [super free]; } /* * Ugly function to write PostScript error. */ void WritePostScriptError (char *errStr, int errStrLength, NXHandler *errorState) { char *streamAddr; int streamLength, maxLength; NXStream *errorStream; if ((errorState->code == dps_err_ps) && (errorStream = NXOpenMemory (NULL, 0, NX_WRITEONLY))) { DPSPrintErrorToStream (errorStream, (DPSBinObjSeq)(errorState->data2)); NXFlush (errorStream); NXGetMemoryBuffer (errorStream, &streamAddr, &streamLength, &maxLength); if (streamLength > errStrLength-1) streamLength = errStrLength-1; strncpy (errStr, streamAddr, streamLength); errStr[streamLength] = 0; NXCloseMemory (errorStream, NX_FREEBUFFER); } else { sprintf (errStr, "A non-PostScript error while running program."); } } /* * SwitchContextsWithFocus() will make the specified context the current * context and make it focus on the same area the old context was * focused on. */ static void SwitchContextsWithFocus (DPSContext newContext) { float c1x, c1y, c2x, c2y; float winCTM[6]; int realWinNum; GetFocus (&c1x, &c1y, &c2x, &c2y, winCTM, &realWinNum); DPSSetContext (newContext); ReFocus (realWinNum, winCTM, c1x, c1y, c2x, c2y); } - setCurrentDocument:anDoc { currentDocument = anDoc; return self; } #define STATUSLENGTH 200 // Some large number for error string length /* * executeCodeFrom: treats the contents of the specified text object as * a PostScript program and executes it. The code is copied from the * text object into a memory stream and then sent to the server using * DPSWriteData(). * * For protection against errors, the PostScript code is interpreted in a * context separate from the Application's own context (which is created * in the +new method of Application). We first focus on the cache, * note the various parameters (global window number, the transformation * matrix, and the clip path), and then switch to the alternate context and * reapply the parameters to establish a focus on the same area. * * Protection against PostScript errors is provided through the use of * NX_DURING/NX_HANDLER. If an error occurs, we immediately blast the * second context and report the first error encountered. If no errors * occur during the execution, then we hang on to the context as it can * be reused. * * Note that the NXEPSImageRep class provides a similar (but more powerful) * sort of functionality for EPS files. Use that class rather than the code * here if you wish to make use of EPS files in your application. This code * here is meant for unstructured, short pieces of PostScript code, * eactly the kind that Yap encounters... */ - executeCodeFrom:docObject andReturnBB: (float *)llx : (float *)lly : (float *)urx : (float *)ury usertime: (int *) utime { // int utime; /* Time taken to execute the code */ char status[STATUSLENGTH]; /* Array for error messages */ NXStream *psStream; /* Memory stream for the PostScript */ char *psBuffer; /* The buffer used by the stream */ int psLen; /* And the length of this buffer */ static DPSContext yapContext = NULL; /* The second context */ DPSContext curContext = DPSGetCurrentContext(); NXHandler exception; currentDocument = docObject; // save which window's PS code is rendered. /* Order front output window */ [[self window] orderFront:self]; /* Open a memory stream and write the text into it... */ if (psStream = NXOpenMemory (NULL, 0, NX_WRITEONLY)) { int dummy; NXPrintf (psStream, "/yaptime usertime def " "/-showpage {} def\n"); [[[docObject document] docView] writeText:psStream]; NXPrintf (psStream, "\n/yaptime usertime yaptime sub def\n"); NXFlush (psStream); NXGetMemoryBuffer (psStream, &psBuffer, &psLen, &dummy); } else { [[self window] setTitle:"Can't open memory stream!"]; return self; } [[self window] setTitle:"BUSY"]; /* Lock focus on the cache. If user wishes to see the cache, bring it up. */ [[cache contentView] lockFocus]; if (clearCache) NXEraseRect (&bounds); if (showCache) [[cache center] orderFront:self]; /* Create the second context if it needs to be created. */ if (yapContext == NULL) { const char *app = [NXApp appName]; yapContext = DPSCreateContext(NXGetDefaultValue(app, "NXHost"), NXGetDefaultValue(app, "NXPSName"), NULL, NULL); DPSSetContext (curContext); } /* Focus the second context to whatever the first context is focused on. */ SwitchContextsWithFocus (yapContext); /* This will let us know if there were any errors. */ exception.code = 0; NX_DURING { DPSPrintf(yapContext, "/yapwidth %f def /yapheight %f def\n", bounds.size.width, bounds.size.height); // if(blackBG) // if(meshON) // temporarily taken over // BlackBG(); if(blueON) { DPSPrintf(yapContext, "0 0 0.5 setrgbcolor clippath fill\n"); } if(meshON) ShowMesh(); /* draws point grid */ if(figureBB) SendBBfig(); /* pswrap func containing bbfig */ DPSWriteData(yapContext, psBuffer, psLen); NXPing(); /* This does not return until the execution is done. */ /* If there were any errors, we jump to the handler. */ GetUserTime (utime); /* utime already pointer */ sprintf (status, "BBFig Postscript Output (Execution Time %d ms)", *utime); if(figureBB) GetBBox(llx, lly, urx, ury); /* get BoundingBox; args are pointers */ else { *llx = *lly = 0.0; *urx = *ury = 999.0; *utime = -1; } /* sprintf (status, "%%%%BoundingBox: %5.0f %5.0f %5.0f %5.0f", *llx, *lly, *urx, *ury); */ } NX_HANDLER { exception = NXLocalHandler; /* Make note of the error... */ } NX_ENDHANDLER /* Switch back to the app context. */ DPSSetContext(curContext); /* In case of error, report it and blow the second context away. */ /* -> Destroy the second context anyway */ DPSDestroyContext(yapContext); yapContext = NULL; if (exception.code != 0) { // DPSDestroyContext(yapContext); // yapContext = NULL; WritePostScriptError (status, STATUSLENGTH, &exception); *llx = *lly = 0.0; *urx = *ury = 999.0; *utime = -1; /* error indication */ } if (showCache) [cache orderOut:self]; [[cache contentView] unlockFocus]; [self display]; [[self window] setTitle:status]; NXCloseMemory (psStream, NX_FREEBUFFER); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.