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 bezier is drawn onto. It is
* a subview of the doc view and can be zoomed and reduced.
*
* The buffers are used to allow for fast redrawing and moving.
* BuffeAlpha is a view inside a ClipView in a plain window and
* bufferBeta is a view in a plain window without a ClipView. The
* drawing is performed in the alpha buffer and then this is
* composited into the buffered window. When the bezier is
* selected and changed, the drawing of the new bezier
* occurs in the buffered window. With each mouse drag the
* old image in alpha is first composited into the window and
* then the new bezier drawn atop the old image.
*
* When the image is moved, it is first drawn into the beta
* buffer and then simply composited to a new location. If
* the image is larger than the beta buffer, then the image
* is redrawn instead of using the buffer.
*
* Version: 2.0
* Author: Ken Fromm
* History:
* 03-07-91 Added this comment.
*/
#import "Bezier.h"
#import "HitPointView.h"
#import "DetectApp.h"
#import "DocView.h"
#import "DrawingView.h"
#import "DrawingViewWraps.h"
#import <appkit/Application.h>
#import <appkit/Cell.h>
#import <appkit/Cursor.h>
#import <appkit/Matrix.h>
#import <appkit/View.h>
#import <appkit/ClipView.h>
#import <appkit/Window.h>
#import <appkit/nextstd.h>
#import <dpsclient/wraps.h>
#import <appkit/timer.h>
@implementation DrawingView
static char fontname[ ] = "ControlPointsFont";
/*
* 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];
}
}
static 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);
}
/*
* Instance variables - hitPoint holds the usre path for
* hit detection while drawUpath is a scratch buffer used for
* several misc. purposes.
*/
- initFrame:(NXRect *) frm
{
[super initFrame:frm];
[[self allocateGState] setClipping:NO];
[self createGrid];
PSWDefineFont(fontname);
/*
* Normally these buffers might be allocated in the Application subclass and kept
* as an application wide resource for use with multiple documents. It is allocated
* here to simplify the code.
*/
NX_MALLOC(drawUpath.pts, float, PTS_UPATH_BUFFER);
NX_MALLOC(drawUpath.ops, char, OPS_UPATH_BUFFER);
NX_MALLOC(hitPoint.pts, float, NUM_POINTS_HIT);
NX_MALLOC(hitPoint.ops, char, NUM_OPS_HIT);
[self initializeHitPoint];
bufferAlpha = [NXApp getBufferAlpha];
bufferBeta = [NXApp getBufferBeta];
selected = NO;
timer = NULL;
return self;
}
/*
* The user path uses relative movements to reduce the number
* of points that have to be inserted each mouse down.
*/
- initializeHitPoint
{
int i;
for (i = 0; i < NUM_POINTS_HIT; i++)
hitPoint.pts[i] = 0;
hitPoint.num_pts = i;
hitPoint.ops[0] = dps_setbbox;
hitPoint.ops[1] = dps_moveto;
hitPoint.ops[2] = dps_rlineto;
hitPoint.ops[3] = dps_rlineto;
hitPoint.ops[4] = dps_rlineto;
hitPoint.ops[5] = dps_closepath;
hitPoint.num_ops = 6;
return self;
}
/*
* Create a cached user path and store it in the server. The grid does not change
* so it can be stored in the server instead of being resent each time. Drawing the
* entire grid each time instead of just the portion that is necessary is faster in
* this case because the cache is every time. When selectively drawing only
* the portion necessary, a different path is cached thereby reducing the number
* of cache hits.
*
*/
- createGrid
{
int i, j, num_pts, num_ops;
float pt;
char *ops;
float *pts;
num_ops = ceil((bounds.size.width/SIZEGRID + bounds.size.height/SIZEGRID) * 2) + 2;
num_pts = num_ops * 2 + 4;
NX_MALLOC(pts, float, num_pts);
NX_MALLOC(ops, char, num_ops);
i = j = 0;
ops[j++] = dps_ucache;
pts[i++] = bounds.origin.x;
pts[i++] = bounds.origin.y;
pts[i++] = bounds.origin.x + bounds.size.width;
pts[i++] = bounds.origin.y + bounds.size.height;
ops[j++] = dps_setbbox;
for (pt = bounds.origin.x; pt < bounds.origin.x + bounds.size.width; pt += SIZEGRID)
{
pts[i++] = pt;
pts[i++] = bounds.origin.y;
ops[j++] = dps_moveto;
pts[i++] = pt;
pts[i++] = bounds.origin.y + bounds.size.height;
ops[j++] = dps_lineto;
}
for (pt = bounds.origin.y; pt < bounds.origin.y + bounds.size.height; pt += SIZEGRID)
{
pts[i++] = bounds.origin.x;
pts[i++] = pt;
ops[j++] = dps_moveto;
pts[i++] = bounds.origin.x + bounds.size.width;
pts[i++] = pt;
ops[j++] = dps_lineto;
}
/* Store it as a user object first by placing it on the stack and then defining it */
PSWSetUpath(pts, i, ops, j);
gridUpath = DPSDefineUserObject(gridUpath);
if (pts)
NX_FREE(pts);
if (ops)
NX_FREE(ops);
return self;
}
/* When the bezier is created, make sure it can be seen. */
- createObject
{
NXRect visRect;
[self getVisibleRect:&visRect];
objectId = [[Bezier alloc] initFrame:&visRect];
return self;
}
- free
{
if (drawUpath.pts)
NX_FREE(drawUpath.pts);
if (drawUpath.ops)
NX_FREE(drawUpath.ops);
if (hitPoint.pts)
NX_FREE(hitPoint.pts);
if (hitPoint.ops)
NX_FREE(hitPoint.ops);
return [super free];
}
/*
* Change the menu title and toggle the trace boolean
*/
- traceDetect:sender
{
if (!tracedetect)
[[sender selectedCell] setTitle:"Hit Detection On"];
else
[[sender selectedCell] setTitle:"Hit Detection Off"];
tracedetect = !tracedetect;
return self;
}
- traceDraw:sender
{
if (!tracedraw)
[[sender selectedCell] setTitle:"Drawing On"];
else
[[sender selectedCell] setTitle:"Drawing Off"];
tracedraw = !tracedraw;
return self;
}
- drawGrid:sender
{
drawgrid = [sender state];
[self display];
return self;
}
- compositeBufferAlpha:(NXRect *) r
{
NXRect viewFrame;
if (!r)
{
[self getVisibleRect:&viewFrame];
r = &viewFrame;
}
[bufferAlpha lockFocus];
compositeBuffer([self gState], r, &r->origin, NX_COPY);
[bufferAlpha unlockFocus];
return self;
}
/*
* When the drawing view moves, then move bufferAlpha so that
* the composites from bufferAlpha are taken from the correct spot.
* The beta buffer does not need to be moved because the drawing
* always starts in the lower left corner of the beta buffer.
*/
- moveTo:(NXCoord)x :(NXCoord)y
{
[super moveTo:x :y];
[bufferAlpha moveTo:x :y];
return self;
}
/*
* A scale that happens to this view should be reflected in the alpha and
* beta buffers.
*/
- scale:(NXCoord)x :(NXCoord)y
{
[super scale:x :y];
[bufferAlpha scale:x :y];
[bufferBeta scale:x :y];
return self;
}
/*
* A sizeTo should also be reflected in the alpha and beta buffers.
*/
- sizeTo:(NXCoord)x :(NXCoord)y
{
[super sizeTo:x :y];
[bufferAlpha sizeTo:x :y];
[bufferBeta sizeTo:x :y];
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;
}
- (BOOL) isScrolling
{
return scrolling;
}
/*
* 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, tooRect;
[self getVisibleRect:&visRect];
if (!NXContainsRect(&visRect, toRect))
{
scrolling = YES;
[window disableFlushWindow];
[self scrollRectToVisible:toRect];
[window reenableFlushWindow];
scrolling = NO;
startTimer(&timer, &timermask, window);
}
else
stopTimer(&timer, &timermask, window);
return self;
}
/*
* Redraws the graphic. The image from the alpha 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 alpha buffer is also scrolled. When the
* alpha 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:(int) pt_num :(NXRect *)redrawRect
{
id copyId;
BOOL tracking = YES;
int old_mask;
NXPoint pt, pt_last, pt_old, delta;
NXRect rect_now, rect_last, rect_scroll, rect_vis;
NXEvent *event;
/*
* Composite the current image in the window into the first buffer.
*/
[self compositeBufferAlpha:NULL];
/*
* Create a copy of the selected object. If we scroll we will need
* to redraw the old curve. If we do not create a copy we will
* not have an old curve.
*/
copyId = [objectId copy];
[copyId copyPts:objectId];
timermask = 0;
if (tracedraw)
DPSTraceContext(DPSGetCurrentContext(), YES);
/*
* The rect_scroll will cause scrolling whenever it goes outside the
* visible portion of the view.
*/
[self getVisibleRect:&rect_vis];
[copyId getScrollRect:pt_num :&rect_scroll];
NXIntersectionRect(&rect_vis, &rect_scroll);
[copyId getBounds:&rect_now withKnobs:YES];
*redrawRect = rect_last = rect_now;
[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)
{
while (tracking)
{
/*
* If its a timer event than use the last point. It will be converted to
* into 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 andNumber:pt_num toView:self];
delta.x = pt.x - pt_last.x;
delta.y = pt.y - pt_last.y;
if (delta.x || delta.y)
{
/* Change the point location and get the new bounds. */
[copyId setPoint:pt_num :&delta];
[copyId getBounds:&rect_now withKnobs:YES];
/* Change the scrolling rectangle. */
NXOffsetRect(&rect_scroll, delta.x, delta.y);
[self scrollToRect:&rect_scroll];
/* Composite the old image and then redraw the new curve. */
compositeBuffer([bufferAlpha gState], &rect_last, &rect_last.origin, NX_COPY);
[self drawObject:NULL for:copyId withUcache:NO];
[self drawControl:NULL for:copyId];
/* Flush the drawing so that it's consistent. */
[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];
[objectId free];
objectId = copyId;
/*
* Figure out the area that has to be redrawn
* (the union of the old and the new rectangles).
*/
NXUnionRect(&rect_now, redrawRect);
return self;
}
/*
* Moves the graphic object. If the selected graphic can fit in the beta
* buffer than the image is drawn into this buffer and then composited
* to each new location. The image is redrawn at the new location
* when the user releases the mouse button. If the selected graphic
* cannot fit in the beta buffer than it is redrawn each time. This can
* happen when the drawing view is scaled upwards.
*
* The offsets constrain the selected object to stay within the dimensions
* of the view.
*/
- moveObject:(NXEvent *)event :(NXRect *)redrawRect
{
BOOL tracking = YES, beta;
int old_mask;
float scale;
NXSize llOffset, urOffset;
NXPoint pt, pt_last, pt_old, delta, delta_scroll;
NXRect rect_now, rect_start, rect_last, rect_scroll, rect_vis;
/*
* Composite the current image in the window
* into the first buffer.
*/
[self compositeBufferAlpha:NULL];
if (tracedraw)
DPSTraceContext(DPSGetCurrentContext(), YES);
/* Check whether the object can fit in the second buffer. */
[[bufferBeta superview] getFrame:&rect_now];
[objectId getBounds:&rect_start withKnobs:YES];
scale = [superview scale];
if (rect_now.size.width > rect_start.size.width * scale &&
rect_now.size.height > rect_start.size.height * scale)
{
[bufferBeta setDrawOrigin:rect_start.origin.x :rect_start.origin.y];
[bufferBeta lockFocus];
PSsetgray(NX_WHITE);
PSsetalpha(0.0);
NXRectFill(&rect_start);
PSsetalpha(1.0);
[self drawObject:NULL for:objectId withUcache:NO];
[self drawControl:NULL for:objectId];
[bufferBeta unlockFocus];
beta = YES;
}
else
beta = NO;
/*
* Get the scrolling rectangle. If it turns out to be the visible portion of the window
* then reduce it a bit so that the user is not playing pong when trying to
* move the image.
*/
[objectId getScrollRect:-1 :&rect_scroll];
[self getVisibleRect:&rect_vis];
if (NXContainsRect(&rect_scroll, &rect_vis))
{
rect_scroll = rect_vis;
NXInsetRect(&rect_scroll, rect_scroll.size.width * .20, rect_scroll.size.height * .20);
}
else
NXIntersectionRect(&rect_vis, &rect_scroll);
*redrawRect = 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;
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;
old_mask = [window addToEventMask:
NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
event = [NXApp getNextEvent:
NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
if (event->type != NX_MOUSEUP)
{
while (tracking)
{
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)
{
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];
/*
* Copy the old image into the window. If using the second buffer, copy
* it atop the old buffer. Otherwise, translate and redraw.
*/
compositeBuffer([bufferAlpha gState], &rect_last, &rect_last.origin, NX_COPY);
if (beta)
{
compositeBuffer([bufferBeta gState], &rect_start, &rect_now.origin,
NX_SOVER);
}
else
{
PSgsave();
PStranslate(rect_now.origin.x - rect_start.origin.x,
rect_now.origin.y - rect_start.origin.y);
[self drawObject:NULL for:objectId withUcache:YES];
[self drawControl:NULL for:objectId];
PSgrestore();
}
/* Flush the drawing so that it's consistent. */
[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);
delta.x = rect_now.origin.x - rect_start.origin.x;
delta.y = rect_now.origin.y - rect_start.origin.y;
[objectId moveAll:&delta];
}
NXUnionRect(&rect_now, redrawRect);
[window setEventMask:old_mask];
return self;
}
/* Check to see whether a control point has been hit. */
- (BOOL) checkControl:(const NXPoint *) p :(int *) pt_num
{
BOOL hit;
float controlsize, hitsetting;
NXRect hitRect;
controlsize = [superview controlPointSize];
hitsetting = [superview hitSetting];
NXSetRect(&hitRect, p->x - hitsetting/2, p->y - hitsetting/2, hitsetting, hitsetting);
hit = [objectId hitControl:&hitRect :pt_num :controlsize];
return hit;
}
/*
* A method in the DrawingView class. Invoked by the mouse down
* event. The mouse point and the current hit setting dimensions
* are placed in the hit point user path before invoking the
* hitObject method of the Bezier object.
*/
- (BOOL) checkObject:(const NXPoint *) p
{
BOOL hit;
float hitsetting;
hitsetting = [superview 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;
if (tracedetect)
DPSTraceContext(DPSGetCurrentContext(), YES);
hit = [objectId hitObject:&hitPoint];
if (tracedetect)
DPSTraceContext(DPSGetCurrentContext(), NO);
return hit;
}
/*
* If the docview is zooming, then scale the drawing view. Else
* check for hit detection on the bezier or the control points.
*/
- mouseDown:(NXEvent *)event
{
BOOL redraw = YES;
int pt_num;
NXPoint p;
NXRect drawRect;
p = event->location;
if ([superview isZooming])
return [nextResponder scaleDrawView:self toPoint:&p];
[self convertPoint:&p fromView:nil];
[self lockFocus];
if (!selected)
{
if ([self checkObject:&p])
{
[objectId getBounds:&drawRect withKnobs:YES];
selected = YES;
}
else
redraw = NO;
}
else
{
if ([self checkControl:&p :&pt_num])
[self redrawObject:pt_num :&drawRect];
else if ([self checkObject:&p])
[self moveObject:event :&drawRect];
else
{
[objectId getBounds:&drawRect withKnobs:YES];
selected = NO;
}
}
/*
* The view has already been focused and we know what
* has to be redrawn so call drawSelf:: instead of display
*/
if (redraw)
{
[self drawSelf:&drawRect :1];
[window flushWindow];
NXPing();
}
[self unlockFocus];
return self;
}
/*
* Draw the object. This is a relatively simple drawing operation. More
* sophiticated drawing apps would probably want to pass the current
* drawing parameters to avoid unnecessarily resetting them with
* each drawing.
*/
- drawObject:(NXRect *)r for:object withUcache:(BOOL)flag
{
[object drawObject:r withUcache:flag];
return self;
}
/*
* 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.
*/
- drawControl:(NXRect *)r for:object
{
int i;
NXPoint lastpoint;
PSselectfont(fontname, [superview controlPointSize]);
PSsetgray(NX_BLACK);
PSsetlinewidth(0.15);
lastpoint.x = 0;
lastpoint.y = 0;
drawUpath.num_ops = 0;
drawUpath.num_pts = 0;
[object putControlUPath:&drawUpath forRect:r :&lastpoint];
if (drawUpath.num_ops > 0)
{
drawUpath.ops[drawUpath.num_ops] = 0;
drawUpath.pts[drawUpath.num_pts] = 0;
drawUpath.pts[drawUpath.num_pts + 1] = 0;
PSWDrawControlPoints(drawUpath.pts[0], drawUpath.pts[1],
&drawUpath.pts[2], drawUpath.num_pts, drawUpath.ops);
}
drawUpath.num_ops = 0;
drawUpath.pts[0] = 99999;
drawUpath.pts[1] = 99999;
drawUpath.pts[2] = -99999;
drawUpath.pts[3] = -99999;
drawUpath.num_pts = 4;
[object putControlLinesUPath:&drawUpath forRect:r];
if (drawUpath.num_ops > 0)
{
DPSDoUserPath(&drawUpath.pts[4], drawUpath.num_pts - 4, dps_float,
drawUpath.ops, drawUpath.num_ops, drawUpath.pts, dps_ustroke);
}
return self;
}
/*
* Compare the bounds of the object with the rectangle to draw in order to
* eliminate unnecessary drawing.
*/
- drawSelf:(NXRect *)r :(int) count
{
NXRect objRect;
if (tracedraw)
DPSTraceContext(DPSGetCurrentContext(), YES);
PSsetgray(NX_WHITE);
NXRectFill(r);
if (drawgrid)
{
PSgsave();
PSsetgray(COLORGRID);
PSsetlinewidth(WIDTHGRID);
NXRectClip(r);
PSWUpathStroke(gridUpath);
PSgrestore();
}
[self drawObject:r for:objectId withUcache:YES];
if (selected && NXDrawingStatus == NX_DRAWING)
[self drawControl:r for:objectId];
if (scrolling)
[self compositeBufferAlpha:r];
if (tracedraw)
{
if (![superview isTraceZooming] || ![superview isZooming])
DPSTraceContext(DPSGetCurrentContext(), NO);
}
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.