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.