This is DrawingView.m in view mode; [Download] [Up]
/* * (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved. * * (b) If this Sample Code is distributed as part of the Display PostScript * System Software Development Kit from Adobe Systems Incorporated, * then this copy is designated as Development Software and its use is * subject to the terms of the License Agreement attached to such Kit. * * (c) If this Sample Code is distributed independently, then the following * terms apply: * * (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. */ /* * DrawingView.m * * This view represents the page that the image is drawn onto. It is * a subview of the DocView. The DocView is the document view of * the ClipView. This view's real size grows with the scale but the * bounds always stays the same. * * An offscreen buffer, bufferId, is used to draw into and then * this buffer is composited onscreen. This technique allows for the * separation of the static drawing from the temporal drawing (modal * loop redrawing and control point display). The static drawing takes * place in the buffer while the temporal drawing takes place in this view. * The static drawing is stuff that is complex and that will stay around for * a while. Saving this drawing in a buffer elimates having to redraw it * for something simple like drawing a control point or something. * (The bufferId is the content view of a plain window the size of the * content view of this view's window.) * * Version: 2.0 * Author: Ken Fromm * History: * 03-17-91 Added this comment and fixed the preview section. */ #import "DrawingView.h" #import "DocView.h" #import "Document.h" #import "GraphicImport.h" #import "GraphicList.h" #import "ImportPanel.h" #import "SaveAsPanel.h" #import "epsf.h" #import "DrawingViewWraps.h" #import <appkit/Cell.h> #import <appkit/ClipView.h> #import <appkit/NXCursor.h> #import <appkit/NXImage.h> #import <appkit/NXBitmapImageRep.h> #import <appkit/Text.h> #import <appkit/Pasteboard.h> #import <appkit/PrintInfo.h> #import <appkit/nextstd.h> #import <dpsclient/dpsclient.h> #import <dpsclient/wraps.h> #import <math.h> extern char ControlFont[ ]; extern const NXRect DefaultContentRect; static char EpsfPboard[] = "Epsf Pasteboard Type"; static char EpsfProcSet[] = "EPSF_Illustrator_abbrev 0 0"; /* * Timers used to automatically scroll when the mouse is * outside the drawing view and not moving. */ static void startTimer(NXTrackingTimer ** timer, int *timermask, id window) { if (!*timer) { *timer = NXBeginTimer(NULL, 0.15, 0.2); *timermask = NX_TIMERMASK; [window addToEventMask:NX_TIMERMASK]; } } static void stopTimer(NXTrackingTimer **timer, int *timermask, id window) { if (*timer) { NXEndTimer(*timer); *timer = NULL; *timermask = 0; [window removeFromEventMask:NX_TIMERMASK]; } } void compositeBuffer(int gstate, const NXRect *srce, const NXPoint *dest, int op) { PScomposite(NX_X(srce), NX_Y(srce), NX_WIDTH(srce), NX_HEIGHT(srce), gstate, dest->x, dest->y, op); } /* * The next two procedures are for the drawing buffer. * This buffer is used to prevent unnecessary drawing by * retaining images within offscreen windows. * * Create a plain window the size of the rectangle passed in and * then insert a view into the window as a subview. A clip view * is swapped for the content view so the buffer can be scrolled * when this view is scrolled. */ static id createBuffer(const NXSize *winSize) { id buffer, clipview, window; NXRect contRect; contRect.origin.x = contRect.origin.y = 0; contRect.size = *winSize; window = [Window newContent:&contRect style:NX_PLAINSTYLE backing:NX_RETAINED buttonMask:0 defer:NO] ; buffer = [[[[View newFrame:&contRect] allocateGState] setFlipped:NO] setClipping:NO]; clipview = [[[ClipView new] setFlipped:NO] setDisplayOnScroll:NO]; [[window setContentView:clipview ] free]; [clipview setDocView:buffer]; [clipview allocateGState]; [window display]; return buffer; } /* * Resize the buffer and its window. Called when this view's * window resizes. (The buffer is enlarged by a bit as an * added insurance.) */ void resizeBuffer(id buffer, const NXSize*newSize) { NXRect frameRect, contRect; [buffer getFrame:&frameRect]; if (newSize->width > frameRect.size.width || newSize->height > frameRect.size.height) { contRect.origin.x = contRect.origin.y = 0.0; contRect.size = *newSize; NXInsetRect(&contRect, -10.0, -10.0); [Window getFrameRect:&frameRect forContentRect:&contRect style:NX_PLAINSTYLE]; [[buffer window] sizeWindow:frameRect.size.width :frameRect.size.height]; [buffer sizeTo:newSize->width :newSize->height]; } } @implementation DrawingView +newFrame:(const NXRect *) frameRect { self = [super newFrame:frameRect]; [[self allocateGState] setClipping:NO]; graphiclistId = [GraphicList new]; selectedlistId = [GraphicList new]; hitPoint = [NXApp hitPoint]; upathBuffer = [NXApp upathBuffer]; bufferId = createBuffer(&DefaultContentRect.size); return self; } /* Free any unplaced imported object. */ - free { [graphicId free]; [[graphiclistId freeObjects] free]; [selectedlistId free]; [[bufferId window] free]; return [super free]; } /* * Aids for understanding the operation of the application. * Moves the offscreen buffer onscreen. */ - showBuffer:sender { [[bufferId window] moveTo:10 :10]; [[bufferId window] orderFront:self]; [window orderFront:self]; return self; } - hideBuffer:sender { [[bufferId window] orderOut:self]; return self; } /* * Returns the size of the control point scaled to reflect the current scale. */ - (float) controlPointSize { return FONTSIZE * (1.0/[superview scale]); } /* * Scale the hit setting. Using an unscaled hit setting would be like * using a boxing glove on a 400% scale. */ - (float) hitSetting { return ([NXApp hitSetting] * (1.0/[superview scale])); } - buffer { return bufferId; } - image { return imageId; } - setDirty:(BOOL) flag { dirty = flag; [window setDocEdited:flag]; return self; } - (BOOL)isDirty { return dirty; } - (BOOL)isEmpty { return [graphiclistId count] == 0; } - (BOOL)isSelected { return [selectedlistId count] > 0; } /* * Set the cursor to be the intersection of the bounds * and the visible portion of this view. */ - resetCursorRects { NXRect visRect; [self getVisibleRect:&visRect]; NXIntersectionRect(&bounds, &visRect); [self addCursorRect:&visRect cursor:[NXApp cursor]]; return self; } /* * When the drawing view moves, then move bufferId so that * the composites from bufferId are taken from the correct spot. */ - moveTo:(NXCoord)x :(NXCoord)y { [super moveTo:x :y]; [bufferId moveTo:x :y]; return self; } /* * A scale as well needs to be reflected in the offscreen buffer. */ - scale:(NXCoord)x :(NXCoord)y { [super scale:x :y]; [bufferId scale:x :y]; return self; } /* * This method copies the PostScript code for the graphics and writes it to the * stream passed in. Includes the preview image when appropriate. */ - writePSToStream:(NXStream *) stream { id nximageId, templist; NXRect bbox; if (stream && [selectedlistId count]) { nximageId = NULL; imageId = NULL; templist = graphiclistId; graphiclistId = selectedlistId; [graphiclistId getBounds:&bbox]; if ([[SaveAsPanel new] format] == SAVE_EPSPREVIEW) { nximageId = [[NXImage alloc] initSize:&bbox.size]; [nximageId useCacheWithDepth:NX_TwoBitGrayDepth]; if ([nximageId lockFocus]) { PStranslate(-bbox.origin.x, -bbox.origin.y); PSsetgray(NX_WHITE); NXRectFill(&bbox); [graphiclistId drawObject:&bbox withFlags:NOFLAGS inView:self]; imageId = [[NXBitmapImageRep alloc] initData:NULL fromRect:&bbox]; [nximageId unlockFocus]; } } [self copyPSCodeInside:&bbox to:stream]; selectedlistId = graphiclistId; graphiclistId = templist; [nximageId free]; [imageId free]; imageId = NULL; } return self; } /* Pasteboard-related target/action methods */ /* Calls copy:, then removes and frees the objects in the selection. */ - cut:sender { float knobsize; NXRect rect; if ([selectedlistId count]) { knobsize = -[self controlPointSize]/2; [selectedlistId getBounds:&rect]; NXInsetRect(&rect, knobsize, knobsize); if (sender != self) [self copy:sender]; [graphiclistId removeObjectsIn:selectedlistId]; [selectedlistId freeObjects]; [self display:&rect :1]; [self setDirty:YES]; return self; } return nil; } /* Calls cut bypassing the copy. */ - delete:sender { return [self cut:self]; } /* * Puts all the objects in the selected list into the Pasteboard by * archiving the list. See the draw program for placing the objects * in the Pasteboard as a PostScript image. Much of the work is * available already. The right hooks just have to be added. */ - copy:sender { BOOL error = YES; id pasteboardId = [Pasteboard new]; char *dataptr; const char *types[1]; int length, maxlen; NXStream *stream; NXTypedStream *ts; if ([selectedlistId count]) { types[0] = EpsfPboard; stream = NXOpenMemory(NULL, 0, NX_WRITEONLY); if (stream) { ts = NXOpenTypedStream(stream, NX_WRITEONLY); if (ts) { NXWriteRootObject(ts, selectedlistId); NXCloseTypedStream(ts); NXGetMemoryBuffer(stream, &dataptr, &length, &maxlen); [pasteboardId declareTypes:types num:2 owner:[self class]]; [pasteboardId writeType:EpsfPboard data:dataptr length:length]; error = NO; } NXCloseMemory(stream, NX_FREEBUFFER); } } return (error ? nil : self); } /* Routines to check the types in the Pasteboard */ static BOOL matchPasteType(char *const *types, const char *type) { if (types) while (*types) if (!strcmp(*types++, type)) return YES; return NO; } /* * Validates the pasteboard types and returns the preferred one. */ static const char *drawPasteType(char *const *types) { if (matchPasteType(types, EpsfPboard)) return EpsfPboard; if (matchPasteType(types, NXPostScriptPboard)) return NXPostScriptPboard; if (matchPasteType(types, NXTIFFPboardType)) return NXTIFFPboardType; return NULL; } /* * Pastes any type available from the Pasteboard into the DrawingView. * If the type in the Pasteboard is the internal type, then the objects * are simply added to the selected list and graphic list. If it is of PostScript * or Tiff type then create a GraphicImport object using the contents of the * Pasteboard. */ - paste:sender { id objectId, pastelistId, pasteboardId = [Pasteboard new]; char *dataptr; const char *type; int length; float knobsize; NXPoint offset; NXRect drawRect, visRect; NXStream *stream; NXTypedStream *ts; type = drawPasteType([pasteboardId types]); if (type) { [self lockFocus]; [self deselectObject:selectedlistId]; [self unlockFocus]; [pasteboardId readType:type data:&dataptr length:&length]; stream = NXOpenMemory(dataptr, length, NX_READONLY); if (strcmp(type, EpsfPboard) == 0) { ts = NXOpenTypedStream(stream, NX_READONLY); pastelistId = NXReadObject(ts); if ([pastelistId count]) { [graphiclistId insertObjectsIn:pastelistId]; [selectedlistId insertObjectsIn:pastelistId]; [selectedlistId setSelected:YES]; } NXCloseTypedStream(ts); } else if (strcmp(type, NXPostScriptPboard) == 0 || strcmp(type, NXTIFFPboardType) == 0) { objectId = [[GraphicImport alloc] initFromStream:stream]; [graphiclistId addObject:objectId]; [selectedlistId addObject:objectId]; [selectedlistId setSelected:YES]; } NXCloseMemory(stream, NX_SAVEBUFFER); if ([selectedlistId count]) { knobsize = -[self controlPointSize]/2; [selectedlistId getBounds:&drawRect]; NXInsetRect(&drawRect, knobsize, knobsize); [self getVisibleRect:&visRect]; offset.x = (visRect.origin.x + visRect.size.width/2.0) - (drawRect.origin.x + drawRect.size.width/2.0); offset.y = (visRect.origin.y + visRect.size.height/2.0) - (drawRect.origin.y + drawRect.size.height/2.0); [selectedlistId moveAll:&offset]; drawRect.origin.x += offset.x; drawRect.origin.y += offset.y; [self display:&drawRect :1]; [self setDirty:YES]; } } else return nil; return self; } /* * Selects all the items in the graphiclistId. */ - selectAll:sender { int i; float knobsize; NXRect rect; i = [graphiclistId count]; if (i && i > [selectedlistId count]) { [selectedlistId free]; selectedlistId = [graphiclistId copy]; [selectedlistId setSelected:YES]; knobsize = -[self controlPointSize]/2; [selectedlistId getBounds:&rect]; NXInsetRect(&rect, knobsize, knobsize); [self lockFocus]; [self drawControl:selectedlistId forRect:&rect withFlags:NOFLAGS]; [self unlockFocus]; [window flushWindow]; } return self; } /* * Brings each of the items in the selected list to the front of the * graphics list. The first item selected will be at the front of the * graphics list with the others following immediately behind. */ - bringToFront:sender { id object; BOOL change = NO; int i, j, index; float knobsize; NXRect rect; j = [selectedlistId count]; for (i = 0; i < j; i++) { object = [selectedlistId objectAt:i]; index = [graphiclistId indexOf:object]; if (index != i) { [graphiclistId removeObjectAt:index]; [graphiclistId insertObject:object at:i]; change = YES; } } if (change) { knobsize = -[self controlPointSize]/2; [selectedlistId getBounds:&rect]; NXInsetRect(&rect, knobsize, knobsize); [self display:&rect :1]; [self setDirty:YES]; } return self; } /* * Moves each of the items in the selected list to the back of the * graphics list. The last item selected will be at the back of the * graphics list with the others following immediately ahead. */ - sendToBack:sender { id object; BOOL change = NO; int numS, numG, i, index; float knobsize; NXRect rect; numS = [selectedlistId count] - 1; numG = [graphiclistId count] - 1; for (i = numS; i >= 0; i--) { object = [selectedlistId objectAt:i]; index = [graphiclistId indexOf:object]; if (numG - index != numS - i) { [graphiclistId removeObjectAt:index]; [graphiclistId insertObject:object at:(numG - (numS - i))]; change = YES; } } if (change) { knobsize = -[self controlPointSize]/2; [selectedlistId getBounds:&rect]; NXInsetRect(&rect, knobsize, knobsize); [self display:&rect :1]; [self setDirty:YES]; } return self; } - makeOriginalUsing:(SEL) aMethod { float knobsize; NXRect rect_start, rect_end; knobsize = -[self controlPointSize]/2; [selectedlistId getBounds:&rect_start]; NXInsetRect(&rect_start, knobsize, knobsize); if ([selectedlistId makeObjectsPerform:aMethod]) { [selectedlistId getBounds:&rect_end]; NXInsetRect(&rect_end, knobsize, knobsize); NXUnionRect(&rect_start, &rect_end); [self lockFocus]; [self drawSelf:&rect_end :1]; [self unlockFocus]; [self setDirty:YES]; [window flushWindow]; } return self; } /* * Sizes the selected objects to have their original sizes. */ - originalSize:sender { return [self makeOriginalUsing:@selector(setOriginalSize)]; } /* * Sizes the selected objects to have the save width/height ratio as their * original bounding boxes. Centers them about their centers. */ - originalRatio:sender { return [self makeOriginalUsing:@selector(setOriginalRatio)]; } /* * Scrolls to rectangle passed in if it is not in visible portion of the view. * If the rectangle is larger in width or height than the view, the scrollRectToVisible * method is not altogether consistent. As a result, the rectangle contains only * the image that was previously visible. */ - scrollToRect:(const NXRect *)toRect { NXRect visRect; [self getVisibleRect:&visRect]; if (!NXContainsRect(&visRect, toRect)) { [window disableFlushWindow]; [self scrollRectToVisible:toRect]; [window reenableFlushWindow]; startTimer(&timer, &timermask, window); } else stopTimer(&timer, &timermask, window); return self; } /* * Constrain the point within the view. An offset is needed because when * an object is moved, it is often grabbed in the center of the object. If the * lower left offset and the upper right offset were not included then part of * the object could be moved off of the view. (In some applications, that might * be allowed but in this one the object is constrained to always lie in the * page.) */ - constrainPoint:(NXPoint *)aPt withOffset:(const NXSize*)llOffset :(const NXSize*)urOffset { float margin; NXPoint viewMin, viewMax; margin = ceil(FONTSIZE/2); viewMin.x = bounds.origin.x + llOffset->width + margin; viewMin.y = bounds.origin.y + llOffset->height + margin; viewMax.x = bounds.origin.x + bounds.size.width - urOffset->width - margin; viewMax.y = bounds.origin.y + bounds.size.height - urOffset->height - margin; aPt->x = MAX(viewMin.x, aPt->x); aPt->y = MAX(viewMin.y, aPt->y); aPt->x = MIN(viewMax.x, aPt->x); aPt->y = MIN(viewMax.y, aPt->y); return self; } /* * Constrain a rectangle within the view. */ - constrainRect:(NXRect *)aRect { float margin; NXPoint viewMin, viewMax; margin = ceil(FONTSIZE/2); viewMin.x = bounds.origin.x + margin; viewMin.y = bounds.origin.y + margin; viewMax.x = bounds.origin.x + bounds.size.width - aRect->size.width - margin; viewMax.y = bounds.origin.y + bounds.size.height - aRect->size.height - margin; aRect->origin.x = MAX(viewMin.x, aRect->origin.x); aRect->origin.y = MAX(viewMin.y, aRect->origin.y); aRect->origin.x = MIN(viewMax.x, aRect->origin.x ); aRect->origin.y = MIN(viewMax.y, aRect->origin.y); return self; } /* * Redraws the graphic. The image from the buffer is composited * into the window and then the changed object is drawn atop the * old image. A copy of the image is necessary because when the * window is scrolled the buffer is also scrolled. When the * buffer is scrolled, the old image might have to be redrawn. * As a result, a copy is created and the changes performed on the * copy. Care is taken to limit the amount of area that must be * composited and redrawn. A timer is started is the scrolling rect * moves outside the visible portion of the view. */ - redrawObject:objectId :(int) pt_num { id copyId; BOOL tracking, dirtyFlag = NO; int old_mask; float knobsize; NXPoint pt, pt_last, pt_old, delta; NXRect rect_now, rect_start, rect_last, rect_scroll, rect_vis; NXEvent *event; timermask = 0; /* * Create a copy of the selected object. If we scroll we will need to redraw * the old object. If we do not create a copy we will not have an old object. * The copy method copies the stream pointer and not the data * if the imported file happens to be copied into the document. */ copyId = [objectId copyTemp]; knobsize = -[self controlPointSize]/2; [copyId getBounds:&rect_start]; NXInsetRect(&rect_start, knobsize, knobsize); rect_now = rect_last = rect_start; /* * The rect_scroll will cause scrolling whenever it goes outside the * visible portion of the view. */ [copyId getScrollRect:&rect_scroll forPtNum:pt_num]; NXInsetRect(&rect_scroll, knobsize, knobsize); [self getVisibleRect:&rect_vis]; NXIntersectionRect(&rect_vis, &rect_scroll); [copyId getPoint:pt_num :&pt_last]; pt_old = pt_last; old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]; event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]; if (event->type != NX_MOUSEUP) { tracking = YES; while (tracking) { /* * If its a timer event than use the last point. It will be converted to * the view's coordinate so it will appear as a new point. */ if (event->type == NX_TIMER) pt = pt_old; else pt = pt_old = event->location; [self convertPoint:&pt fromView:nil]; [copyId constrainPoint:&pt forPtNum:&pt_num inRect:&bounds withFlags:event->flags]; delta.x = pt.x - pt_last.x; delta.y = pt.y - pt_last.y; if (delta.x || delta.y) { dirtyFlag = YES; /* Change the point location and get the new bounds. */ [copyId setPoint:pt_num :&delta]; [copyId getBounds:&rect_now]; NXInsetRect(&rect_now, knobsize, knobsize); /* Change the scrolling rectangle. */ NXOffsetRect(&rect_scroll, delta.x, delta.y); [self scrollToRect:&rect_scroll]; /* Composite the old image and then redraw the new one. */ compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY); [self drawObject:copyId forRect:&rect_now withFlags:REDRAWFLAG]; [self drawControl:copyId forRect:&rect_now withFlags:NOFLAGS]; /* Sync up the drawing so it proceeds a little smoother. */ [window flushWindow]; NXPing(); rect_last = rect_now; pt_last = pt; } else stopTimer(&timer, &timermask, window); event = [NXApp getNextEvent:NX_MOUSEUPMASK| NX_MOUSEDRAGGEDMASK|timermask]; tracking = (event->type != NX_MOUSEUP); } stopTimer(&timer, &timermask, window); } [window setEventMask:old_mask]; if (![graphiclistId replaceObject:objectId with:copyId]) [graphiclistId insertObject:copyId at:0]; if (![selectedlistId replaceObject:objectId with:copyId]) [selectedlistId insertObject:copyId at:0]; [objectId freeTemp]; if (dirtyFlag) { /* * The view has already been focused and we know what * has to be redrawn so call drawSelf:: instead of display */ NXUnionRect(&rect_last, &rect_start); [self drawSelf:&rect_start :1]; [window flushWindow]; NXPing(); [self setDirty:YES]; } return self; } /* * Moves the selected objects by performing a translate before drawing * the objects. This approach is used because the objects are drawn * into windows and drawing simply means compositing the windows. * * The offsets constrain the selected object to stay within the dimensions * of the view. */ - moveObject:(NXEvent *)event { BOOL tracking, moveFlag = NO, dirtyFlag = NO; int old_mask; long time; float knobsize; NXSize llOffset, urOffset; NXPoint pt, pt_last, pt_old, delta, delta_scroll; NXRect rect_now, rect_start, rect_last, rect_scroll, rect_vis; /* * Get the scrolling rectangle. Compare it against a reduced version of * the visible rect. If it turns out to be the larger then use the reduced * rectangle so that the user is not playing pong when trying to * move the image. */ knobsize = -[self controlPointSize]/2; [selectedlistId getScrollRect:&rect_scroll forPtNum:-1]; NXInsetRect(&rect_scroll, knobsize, knobsize); [self getVisibleRect:&rect_vis]; NXInsetRect(&rect_vis, rect_vis.size.width * 0.20, rect_vis.size.height * 0.20); if (rect_scroll.size.width > rect_vis.size.width || rect_scroll.size.height > rect_vis.size.height) { NXInsetRect(&rect_scroll, MAX(0, (rect_scroll.size.width - rect_vis.size.width)/2), MAX(0, (rect_scroll.size.height - rect_vis.size.height)/2)); } [selectedlistId getBounds:&rect_start]; NXInsetRect(&rect_start, knobsize, knobsize); rect_now = rect_last = rect_start; delta_scroll.x = rect_scroll.origin.x - rect_now.origin.x; delta_scroll.y = rect_scroll.origin.y - rect_now.origin.y; timermask = 0; time = event->time; pt_last = pt_old = event->location; [self convertPoint:&pt_last fromView:nil]; /* Calculate where the mouse point falls relative to the object. */ llOffset.width = pt_last.x - rect_start.origin.x; llOffset.height = pt_last.y - rect_start.origin.y; urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x; urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y; /* Return nil if the the mouse was not dragged. */ old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]; event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]; if (event->type != NX_MOUSEUP) { tracking = YES; while (tracking) { /* * Only move the object if a certain amount of time has elapsed * between mouse down and mouse up. Prevents accidently * moving objects when selecting. */ if (event->time - time > MOVE_INTERVAL) { moveFlag = YES; if (event->type == NX_TIMER) pt = pt_old; else pt = pt_old = event->location; [self convertPoint:&pt fromView:nil]; [self constrainPoint:&pt withOffset:&llOffset :&urOffset]; [self constrainPoint:&pt_last withOffset:&llOffset :&urOffset]; delta.x = pt.x - pt_last.x; delta.y = pt.y - pt_last.y; if (delta.x || delta.y) { dirtyFlag = YES; NXOffsetRect(&rect_now, delta.x, delta.y); [self constrainRect:&rect_now]; rect_scroll.origin.x = rect_now.origin.x + delta_scroll.x; rect_scroll.origin.y = rect_now.origin.y + delta_scroll.y; [self scrollToRect:&rect_scroll]; /* * Composite the old image into the window and then * translate the user space and draw the objects. */ compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY); PSgsave(); PStranslate(rect_now.origin.x - rect_start.origin.x, rect_now.origin.y - rect_start.origin.y); [self drawObject:selectedlistId forRect:&rect_start withFlags:MOVEFLAG]; [self drawControl:selectedlistId forRect:&rect_start withFlags:NOFLAGS]; PSgrestore(); [window flushWindow]; NXPing(); rect_last = rect_now; pt_last = pt; } else stopTimer(&timer, &timermask, window); } event = [NXApp getNextEvent:NX_MOUSEUPMASK| NX_MOUSEDRAGGEDMASK|timermask]; tracking = (event->type != NX_MOUSEUP); } stopTimer(&timer, &timermask, window); if (dirtyFlag) { delta.x = rect_now.origin.x - rect_start.origin.x; delta.y = rect_now.origin.y - rect_start.origin.y; [selectedlistId moveAll:&delta]; /* * The view has already been focused and we know what * has to be redrawn so call drawSelf:: instead of display */ NXUnionRect(&rect_now, &rect_start); [self drawSelf:&rect_start :1]; [window flushWindow]; NXPing(); [self setDirty:YES]; } } [window setEventMask:old_mask]; /* * Return nil if the elapsed time is less than the amount necessary to * consider the action a move. */ if (moveFlag) return self; else return nil; } -eraseRotatePoint:(NXRect *) drawnRect { float scale; NXRect rotateRect; scale = 1.0 / [superview scale]; rotateRect.origin.x = rotatePoint.x - 8.0 * scale; rotateRect.origin.y = rotatePoint.y - 8.0 * scale; rotateRect.size.width = rotateRect.size.height = 16.0 * scale; if (!NXContainsRect(drawnRect, &rotateRect)) { compositeBuffer([bufferId gState], &rotateRect, &rotateRect.origin, NX_COPY); [self drawControl:selectedlistId forRect:&rotateRect withFlags:NOFLAGS]; } return self; } -drawRotatePoint { float scale; NXPoint compPt; scale = 1.0 / [superview scale]; compPt.x = rotatePoint.x - 8.0 * scale; compPt.y = rotatePoint.y - 8.0 * scale; [[[NXApp getCursor:OP_ROTATE1] image] composite:NX_SOVER toPoint:&compPt]; return self; } /* * Mark the point about which to rotate. Employ gravity near any * control points. */ - rotateObjectStart:(NXEvent *)event { id objectId; int pt_num; rotatePoint = event->location; [self convertPoint:&rotatePoint fromView:nil]; [self lockFocus]; if (objectId = [self checkControl:&rotatePoint :&pt_num]) [objectId getPoint:pt_num :&rotatePoint]; [self drawRotatePoint]; [self unlockFocus]; [window flushWindow]; return self; } /* * Rotates the selected graphics about the rotatePoint selected * from the previous mouse down. The image from the buffer * is composited into the window and then the rotated object is * drawn atop the old image. * * Care is taken to limit the amount of area that must be * composited and redrawn. A timer is started is the scrolling rect * moves outside the visible portion of the view. */ - rotateObject:(NXEvent *)event { id slist; BOOL tracking, dirtyFlag = NO; int old_mask; float knobsize, marginsize; float radians_start, radians_last, radians_delta; NXPoint pt, pt_old, d; NXRect rect_now, rect_start, rect_last, rect_scroll; slist = [selectedlistId copyTemp]; timermask = 0; marginsize = (1.0/[superview scale] * SCROLL_MARGIN)/2; rect_scroll.size.width = rect_scroll.size.height = marginsize * 2; knobsize = -[self controlPointSize]/2; [slist getBounds:&rect_start]; NXInsetRect(&rect_start, knobsize, knobsize); rect_now = rect_last = rect_start; pt_old = pt = event->location; [self convertPoint:&pt fromView:nil]; d.x = pt.x - rotatePoint.x; d.y = pt.y - rotatePoint.y; radians_start = 0; if (d.x || d.y) radians_start = atan2(d.y,d.x); radians_last = radians_start; [self lockFocus]; old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]; event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]; if (event->type != NX_MOUSEUP) { tracking = YES; while (tracking) { /* * If its a timer event than use the last point. It will be converted to * the view's coordinate so it will appear as a new point. */ if (event->type == NX_TIMER) pt = pt_old; else pt = pt_old = event->location; [self convertPoint:&pt fromView:nil]; d.x = pt.x - rotatePoint.x; d.y = pt.y - rotatePoint.y; if (d.x || d.y) { dirtyFlag = YES; radians_delta = atan2(d.y, d.x) - radians_last; [slist constrainAngle:&radians_delta withFlags:event->flags]; /* Rotate the object about the given point at the given angle. */ [slist rotateAboutPoint:&rotatePoint withAngle:radians_delta]; [slist getBounds:&rect_now]; NXInsetRect(&rect_now, knobsize, knobsize); /* Make the scrolling rectangle the mouse point with a little margin. */ rect_scroll.origin.x = pt.x - marginsize; rect_scroll.origin.y = pt.y - marginsize; [self scrollToRect:&rect_scroll]; /* Composite the old image and then redraw the new one. */ NXIntegralRect(&rect_last); compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY); [self drawObject:slist forRect:&rect_now withFlags:REDRAWFLAG]; [self drawControl:slist forRect:&rect_now withFlags:NOFLAGS]; [self drawRotatePoint]; /* Sync up the drawing so it proceeds a little smoother. */ [window flushWindow]; NXPing(); rect_last = rect_now; radians_last = radians_delta + radians_last; } else stopTimer(&timer, &timermask, window); event = [NXApp getNextEvent:NX_MOUSEUPMASK| NX_MOUSEDRAGGEDMASK|timermask]; tracking = (event->type != NX_MOUSEUP); } stopTimer(&timer, &timermask, window); } [window setEventMask:old_mask]; [graphiclistId replaceObjectsIn:selectedlistId with:slist]; [selectedlistId freeTemp]; [selectedlistId free]; selectedlistId = slist; if (dirtyFlag) { /* * The view has already been focused and we know what * has to be redrawn so call drawSelf:: instead of display */ NXUnionRect(&rect_last, &rect_start); [self drawSelf:&rect_start :1]; [self eraseRotatePoint:&rect_start]; [window flushWindow]; NXPing(); [self setDirty:YES]; } [self unlockFocus]; return self; } /* Check to see whether a control point has been hit. */ - checkControl:(const NXPoint *) p :(int *) pt_num { float hitsetting; NXRect hitRect; hitsetting = [self hitSetting]; NXSetRect(&hitRect, p->x - hitsetting/2, p->y - hitsetting/2, hitsetting, hitsetting); return [selectedlistId hitControl:&hitRect :pt_num forSize:[self controlPointSize]]; } /* * Check to see whether an object has been hit. Return the selected list if * any object in it has been hit. Otherwise just return the individual * graphic. */ - checkObject:(const NXPoint *) p in:listId from:(int) start to:(int) end { id objectId; BOOL flag; int i; float hitsetting; hitsetting = [self hitSetting]; /* Bounding Box */ hitPoint->pts[0] = floor(p->x - hitsetting/2); hitPoint->pts[1] = floor(p->y - hitsetting/2); hitPoint->pts[2] = ceil(p->x + hitsetting/2); hitPoint->pts[3] = ceil(p->y + hitsetting/2); /* Moveto */ hitPoint->pts[4] = p->x - hitsetting/2; hitPoint->pts[5] = p->y - hitsetting/2; /* Rlineto's */ hitPoint->pts[7] = hitsetting; hitPoint->pts[8] = hitsetting; hitPoint->pts[11] = -hitsetting; flag = (listId == graphiclistId); for (i = start; i < end; i++) if (objectId = [[listId objectAt:i] hitObject:hitPoint ifNotSelected:flag]) return objectId; return nil; } /* * Set the object as selected, add the object to the selected list and * then draw the control points. */ - selectObject:objectId { NXRect drawRect; [objectId setSelected:YES]; [selectedlistId addObject:objectId]; [self getVisibleRect:&drawRect]; [self drawControl:objectId forRect:&drawRect withFlags:NOFLAGS]; [window flushWindow]; return self; } /* * Remove the object (or objects) from the selected list and redraw the * control points for the visible portion. The control points are drawn * in this view since they can be drawn pretty quickly (and so the don't * need to be cached in an offscreen buffer somewhere). */ - deselectObject:objectId { NXRect drawRect; [self getVisibleRect:&drawRect]; [objectId setSelected:NO]; if (objectId == selectedlistId) [selectedlistId empty]; else [selectedlistId removeObject:objectId]; [self drawControl:selectedlistId forRect:&drawRect withFlags:CLEARFLAG]; [window flushWindow]; return self; } /* * Test for a mouse down hit on either the object or the control points. * This algorithm looks further into the list in order to check for mouse * hits on objects that lie below the currently selected object. */ - testObject:(NXEvent *)event { id objectId, newobjectId; int pt_num, list_index; NXPoint p; p = event->location; [self convertPoint:&p fromView:nil]; [self lockFocus]; if (objectId = [self checkControl:&p :&pt_num]) { [self redrawObject:objectId :pt_num]; } else { if ([selectedlistId count] == 0 || (event->flags & NX_SHIFTMASK) == NX_SHIFTMASK) { if (objectId = [self checkObject:&p in:selectedlistId from:0 to:[selectedlistId count]]) [self deselectObject:objectId]; else if (objectId = [self checkObject:&p in:graphiclistId from:0 to:[graphiclistId count]]) [self selectObject:objectId]; } else { objectId = [self checkObject:&p in:selectedlistId from:0 to:[selectedlistId count]]; if (!objectId || ![self moveObject:event]) { /* Look further into the list first. */ list_index = 0; if (objectId) list_index = [graphiclistId indexOf:objectId]; if ((newobjectId = [self checkObject:&p in:graphiclistId from:list_index to:[graphiclistId count]]) || (newobjectId = [self checkObject:&p in:graphiclistId from:0 to:list_index])) { [self deselectObject:selectedlistId]; [self selectObject:newobjectId]; } else if (!objectId) [self deselectObject:selectedlistId]; } } } [self unlockFocus]; return self; } /* * Free any previously unplaced imported object first. Next, * pass the file name to the factory Tiff or Epsf object to create a new * instance. If successful then remove items from the selected list. * * If a point has been passed in then place the imported file at its * original size with its left corner corresponding to p. If not * then set the NXApp operation to OP_PLACE. The next mouse * down will begin the modal loop for drawing out the sizing rectangle. */ - importFile:(const char *) file at:(NXPoint *) p { BOOL ok; char *end; NXStream *stream; ok = NO; [graphicId free]; graphicId = NULL; if (file) { end = strrchr(file, '.'); if (end) { if ([[ImportPanel new] format] == IMPORT_COPY) { stream = NXMapFile(file, NX_READONLY); if (stream) { if (strncmp(end, ".tiff", 5) == 0 || strncmp(end, ".ps", 3) == 0 || strncmp(end, ".eps", 4) == 0) { graphicId = [[GraphicImport alloc] initFromStream:stream]; [graphicId setFilename:file]; } NXCloseMemory(stream, NX_SAVEBUFFER); } else Notify("Import Error", "Unable to open file."); } else { if (strncmp(end, ".tiff", 5) == 0 || strncmp(end, ".ps", 3) == 0 || strncmp(end, ".eps", 4) == 0) { graphicId = [[GraphicImport alloc] initFromFile:file]; } } if (graphicId) { [self lockFocus]; [self deselectObject:selectedlistId]; [self unlockFocus]; if (p) { [self placeObjectAt:p]; graphicId = NULL; } else [NXApp setOperation:OP_IMPORT]; [window flushWindow]; NXPing(); ok = YES; } } else Notify("Import Error", "Unable to import file. Unrecognized file type."); } return ok ? self : nil; } /* * Place the graphicId with its upper left corner at p; */ - placeObjectAt:(const NXPoint *) p { float knobsize; NXPoint pt; NXRect rect_draw; if (graphicId) { pt = *p; [graphiclistId insertObject:graphicId at:0]; [selectedlistId insertObject:graphicId at:0]; [graphicId getBounds:&rect_draw]; pt.y = pt.y - rect_draw.size.height; [graphicId setOrigin:&pt]; knobsize = -[self controlPointSize]/2; [graphicId getBounds:&rect_draw]; NXInsetRect(&rect_draw, knobsize, knobsize); [self display:&rect_draw :1]; } return self; } /* * Begins the setup for placing an imported file into the document. * The object for the file is created and then waits for the mouse * down to begin placement and sizing. This method first gets the * object and then messages the redrawObject method to draw * the subsequent sizing rectangles. */ - importObject:(NXEvent *)event { float knobsize; NXPoint p; NXRect rect_draw; if (graphicId) { p = event->location; [self convertPoint:&p fromView:nil]; if ([[ImportPanel new] dragToSize]) { [NXApp setOperation:OP_PLACE]; NXSetRect(&rect_draw, p.x, p.y-SIZE_MIN, SIZE_MIN, SIZE_MIN); [graphicId setBounds:&rect_draw]; knobsize = -[self controlPointSize]/2; NXInsetRect(&rect_draw, knobsize, knobsize); [self lockFocus]; [self drawObject:graphicId forRect:&rect_draw withFlags:REDRAWFLAG]; [self drawControl:graphicId forRect:&rect_draw withFlags:NOFLAGS]; [self redrawObject:graphicId :8]; [self unlockFocus]; graphicId = [selectedlistId objectAt:0]; } else [self placeObjectAt:&p]; if ([graphicId error]) { [self lockFocus]; [self deselectObject:graphicId]; [self unlockFocus]; [graphiclistId removeObject:graphicId]; [graphicId free]; } else [self setDirty:YES]; graphicId = NULL; } return self; } /* * Depending on the current operation, check for selection, zoom or * import a file. */ - mouseDown:(NXEvent *)event { int operation; operation = [NXApp operation]; switch (operation) { case OP_SELECT: [self testObject:event]; break; case OP_ZOOMUP: case OP_ZOOMDOWN: [nextResponder scaleDrawView:self withEvent:event]; break; case OP_ROTATE1: [self rotateObjectStart:event]; [NXApp setOperation:OP_ROTATE2]; break; case OP_ROTATE2: [self rotateObject:event]; [NXApp setOperation:OP_ROTATE1]; break; case OP_IMPORT: [self importObject:event]; [NXApp clearOperation]; break; } return self; } /* * Changes the cursor to the reduce cursor if the shift key is * pressed when zooming. */ - flagsChanged:(NXEvent *) event { BOOL shift; int operation; operation = [NXApp operation]; shift = (event->flags & NX_ALTERNATEMASK) == NX_ALTERNATEMASK; if (operation == OP_ZOOMUP && shift) [NXApp setOperation:OP_ZOOMDOWN]; else if (operation == OP_ZOOMDOWN && !shift) [NXApp setOperation:OP_ZOOMUP]; else return [super flagsChanged:event]; return self; } /* * Deletes the selected objects. */ - keyDown:(NXEvent *) event { if ([NXApp operation] == OP_SELECT && event->data.key.charSet == NX_ASCIISET && event->data.key.charCode == NX_DELETE) return [self cut:self]; else return nil; } /* * Draw the control points using the user path buffer to hold * the data for the xyshow. Having the object fill in the data * structure allows for combining the control points for multiple * objects. This increases drawing performance by reducing * the number of operations. But since this operation is performed * after the objects have been drawn, the control points are above * the graphics. This approach may or may not be desired. */ - drawControl:object forRect:(NXRect *)r withFlags:(int)flags { float knobsize; NXPoint lastpoint; NXRect rect; if (r) rect = *r; else [self getVisibleRect:&rect]; if (flags & CLEARFLAG) compositeBuffer([bufferId gState], &rect, &rect.origin, NX_COPY); lastpoint.x = 0; lastpoint.y = 0; upathBuffer->num_ops = 0; upathBuffer->num_pts = 0; knobsize = [self controlPointSize]; NXInsetRect(&rect, -knobsize/2, -knobsize/2); [object putControlPoints:upathBuffer forRect:&rect :&lastpoint]; upathBuffer->ops[upathBuffer->num_ops] = 0; upathBuffer->pts[upathBuffer->num_pts] = 0; upathBuffer->pts[upathBuffer->num_pts + 1] = 0; if (upathBuffer->num_ops > 0) { PSWSetControlPoints(ControlFont, knobsize, NX_BLACK, 0.15); PSWDrawControlPoints(upathBuffer->pts[0], upathBuffer->pts[1], &upathBuffer->pts[2], upathBuffer->num_pts, upathBuffer->ops); } return self; } - drawObject:object forRect:(NXRect *)r withFlags:(int) flags { NXRect rect; if (r) rect = *r; else [self getVisibleRect:&rect]; [object drawObject:&rect withFlags:flags inView:self]; return self; } /* * Fill in the background of the rectangle and then draw the graphics. * If NX_DRAWING, then draw into the buffer and composite into * this view's window. */ - drawSelf:(NXRect *)r :(int) count { if (NXDrawingStatus == NX_DRAWING) { [bufferId lockFocus]; PSsetgray(NX_WHITE); NXRectFill(r); [bufferId unlockFocus]; compositeBuffer([bufferId gState], r, &r->origin, NX_COPY); [bufferId lockFocus]; } [self drawObject:graphiclistId forRect:r withFlags:REFRESHFLAG]; if (NXDrawingStatus == NX_DRAWING) { [bufferId unlockFocus]; compositeBuffer([bufferId gState], r, &r->origin, NX_COPY); [self drawControl:selectedlistId forRect:r withFlags:NOFLAGS]; } return self; } /* * This method is only overridden to eliminate during a copy * the rectclip and gsave/grestore pairing that results from * a lockFocus. These are usually harmless operations but * they interfere with trying to produce Illustrator format files. */ - display:(NXRect *)r :(int) count :(BOOL)flag { if (NXDrawingStatus == NX_COPYING) { [self drawSelf:r :count]; DPSFlushContext(DPSGetCurrentContext()); } else [super display:r :count :flag]; return self; } /* * Do not focus if Printing because we don't want the * scale factored in. */ - (BOOL) lockFocus { if (NXDrawingStatus != NX_PRINTING) return [super lockFocus]; return YES; } - unlockFocus { if (NXDrawingStatus != NX_PRINTING) return [super unlockFocus]; return self; } - (BOOL)acceptsFirstResponder { return YES; } - write:(NXTypedStream *)stream { [super write:stream]; NXWriteTypes(stream, "@", &graphiclistId); return self; } - read:(NXTypedStream *)stream { [super read:stream]; NXReadTypes(stream, "@", &graphiclistId); return self; } - awake { selectedlistId = [GraphicList new]; hitPoint = [NXApp hitPoint]; upathBuffer = [NXApp upathBuffer]; bufferId = createBuffer(&DefaultContentRect.size); return self; } /* * Validates menu commands. It returns NO if the * DrawingView knows that action is not valid now, * otherwise it returns YES. * * Using pastecount and pastevalid prevents having to look into * the Pasteboard every time paste: is validated. */ - (BOOL)validateCommand:menuCell { SEL action = [menuCell action]; id pasteboardId; int count; if ( action == @selector(cut:) || action == @selector(delete:) || action == @selector(copy:) || action == @selector(bringToFront:) || action == @selector(sendToBack:) || action == @selector(originalSize:) || action == @selector(originalRatio:)) { return([selectedlistId count] > 0); } else if (action == @selector(selectAll:)) { return([graphiclistId count] > 0); } else if (action == @selector(paste:)) { pasteboardId = [Pasteboard new]; count = [pasteboardId changeCount]; if (count != pastecount) { pastecount = count; pastevalid = (drawPasteType([pasteboardId types]) != NULL); } return pastevalid; } return YES; } - (BOOL) knowsPagesFirst:(int *) firstPageNum last:(int *) lastPageNum { *firstPageNum = 1; *lastPageNum = 1; return YES; } - (BOOL) getRect:(NXRect *) theRect forPage:(int) page { if (page == 1) { *theRect = bounds; return YES; } return NO; } /* * Used when printing. Returns the global resources used in the document. */ - addResources:(Resource *) resourceDoc { NXAtom string; if (NXDrawingStatus == NX_COPYING) { string = NXUniqueString(EpsfProcSet); if (!resourceDoc[RES_PROCSETS].states[RES_PRESENT]) resourceDoc[RES_PROCSETS].states[RES_PRESENT] = [List new]; [resourceDoc[RES_PROCSETS].states[RES_PRESENT] addObjectIfAbsent:(id) string]; if (!resourceDoc[RES_PROCSETS].states[RES_SUPPLIED]) resourceDoc[RES_PROCSETS].states[RES_SUPPLIED] = [List new]; [resourceDoc[RES_PROCSETS].states[RES_SUPPLIED] addObjectIfAbsent:(id) string]; } return self; } /* * Print the %%DocumentResource comments. A list of the resources is * accumulated from the imported files (only one in this case). * The list is then written to the current context. */ - beginResourceComments:(const NXRect *) bbox { int i, j; Resource resourceDoc[RES_NUMTYPES]; bzero(&resourceDoc, sizeof(resourceDoc)); [self addResources:resourceDoc]; [graphiclistId addResources:resourceDoc for:(NXRect *) bbox]; for (i = 0; i < RES_NUMTYPES; i++) { for (j = 0; j < RES_NUMSTATES; j++) { if (resourceDoc[i].states[j]) { WriteEpsfResource(resourceDoc[i].states[j], i, j); [resourceDoc[i].states[j] free]; } } } return self; } /* * Write out the necessary information. Overridden to include * the fonts from the EPSF files. */ - beginPrologueBBox:(const NXRect *)boundingBox creationDate:(const char *)dateCreated createdBy:(const char *)anApplication fonts:(const char *)fontNames forWhom:(const char *)user pages:(int)numPages title:(const char *)aTitle { time_t clock; DPSContext ctxt; ctxt = DPSGetCurrentContext(); if (!boundingBox) boundingBox = [[NXApp printInfo] paperRect]; if (!dateCreated) { clock = time(0); dateCreated = ctime(&clock); } if (!anApplication) anApplication = [NXApp appName]; if (!user) user = (char *) getlogin(); if (numPages <= 0) numPages = 1; if (!aTitle) aTitle = [[window delegate] filename]; DPSPrintf(ctxt, "%%!PS-Adobe-2.0 EPSF-1.2\n"); DPSPrintf(ctxt, "%%%%Creator: %s\n", anApplication); DPSPrintf(ctxt, "%%%%For: %s\n", user); DPSPrintf(ctxt, "%%%%Title: %s\n", aTitle); DPSPrintf(ctxt, "%%%%CreationDate: %s", dateCreated); DPSPrintf(ctxt, "%%%%BoundingBox: %d %d %d %d\n", (int) floor(boundingBox->origin.x), (int) floor(boundingBox->origin.y), (int) ceil(boundingBox->origin.x + boundingBox->size.width), (int) ceil(boundingBox->origin.y + boundingBox->size.height)); if (NXDrawingStatus == NX_COPYING) DPSPrintf(ctxt, "%%AI3_TemplateBox: %d %d %d %d\n", 306, 396, 306, 396); if (NXDrawingStatus != NX_COPYING) DPSPrintf(ctxt, "%%%%Pages: %d\n", numPages); [self beginResourceComments:boundingBox]; if ([[NXApp printInfo] orientation] == NX_LANDSCAPE) DPSPrintf(ctxt, "%%%%Orientation: Landscape\n"); else DPSPrintf(ctxt, "%%%%Orientation: Portrait\n"); return self; } /* * Includes the abbreviated Illustrator proc set so that * the imported files produced through Save To will print on their * own. Also includes the preview data as a comment when * specified. */ - endHeaderComments { DPSContext ctxt; if (NXDrawingStatus == NX_COPYING) { ctxt = DPSGetCurrentContext(); DPSPrintf(ctxt, "%%%%EndComments\n\n"); DPSPrintf(ctxt, "%%%%BeginProcSet: EPSF_Illustrator_abbrev 0 0\n"); WriteEpsfProcSetDef (); DPSPrintf(ctxt, "%%%%EndProcSet\n\n"); if (imageId && [[SaveAsPanel new] format] == SAVE_EPSPREVIEW) WriteEpsfPreview(imageId); } else [super endHeaderComments]; return self; } /* * If saving in illustrator, override the prologue comment. */ - endPrologue { DPSContext ctxt; if (NXDrawingStatus != NX_COPYING) { ctxt = DPSGetCurrentContext(); DPSPrintf(ctxt, "%%%%EndProlog\n\n"); } else [super endPrologue]; return self; } /* Initialize the Illustrator abbreviated proc set. */ - beginSetup { DPSContext ctxt; if (NXDrawingStatus == NX_COPYING) { ctxt = DPSGetCurrentContext(); DPSPrintf(ctxt, "%%%%BeginSetup\n"); WriteEpsfProcSetInit(); } else [super beginSetup]; return self; } - endSetup { DPSContext ctxt; if (NXDrawingStatus == NX_COPYING) { ctxt = DPSGetCurrentContext(); DPSPrintf(ctxt, "%%%%EndSetup\n"); } else [super endSetup]; return self; } /* Terminate the Illustrator abbreviated proc set. */ - beginTrailer { DPSContext ctxt; if (NXDrawingStatus == NX_COPYING) { ctxt = DPSGetCurrentContext(); DPSPrintf(ctxt, "%%%%Trailer\n"); WriteEpsfProcSetTerm(); } else [super beginTrailer]; return self; } - endTrailer { if (NXDrawingStatus != NX_COPYING) [super endTrailer]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.