This is Scribble.m in view mode; [Download] [Up]
#import "draw.h" /* * This line is just a stub to get genstrings to generate * a .strings file entry for the name of this type of Graphic. * The name is used in the Undo New <Whatever> menu item. * * NXLocalString("Scribble", NULL, "Name of the tool that draws scribbles, i.e., the %s of the New %s operation.") */ @implementation Scribble : Graphic static NXPoint lastPoint; /* used in creating only */ + initialize /* * This bumps the class version so that we can compatibly read * old Graphic objects out of an archive. */ { [Scribble setVersion:1]; return self; } + cursor /* * A Scribble uses a pencil as its cursor. * Since Draw is a subproject in Hippo.bundle, we have * to find the tiff file. -- pfkeb */ { NXBundle *bundle; NXImage *image; NXPoint spot; static NXCursor *cursor = nil; char buffer[MAXPATHLEN+1]; if (!cursor) { bundle = [NXBundle bundleForClass:[self class]]; if ( [bundle getPath:buffer forResource:"pencil" ofType:"tiff"] ) { image = [[NXImage allocFromZone:[self zone]] initFromFile:buffer]; cursor = [NXCursor newFromImage:image]; spot.x = 0.0; spot.y = 15.0; [cursor setHotSpot:&spot]; } } return cursor ? cursor : [super cursor]; } - free { NX_FREE(points); NX_FREE(userPathOps); return [super free]; } - allocateChunk /* * The Scribble's storage is allocated in chunks. * This allocates another chunk. */ { int i, newSize; newSize = length + CHUNK_SIZE; if (points) { NX_ZONEREALLOC([self zone], points, float, newSize << 1); NX_ZONEREALLOC([self zone], userPathOps, char, newSize); } else { NX_ZONEMALLOC([self zone], points, float, newSize << 1); NX_ZONEMALLOC([self zone], userPathOps, char, newSize); } for (i = newSize - 1; i >= length; i--) { userPathOps[i] = dps_rlineto; } return self; } - (float)naturalAspectRatio /* * The Scribble's natural aspect ratio is the one it was created with. */ { return (gFlags.initialized ? ((bbox[2]-bbox[0])/(bbox[3]-bbox[1])) : 0.0); } - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag /* * After the Scribble is created (gFlags.initialized == YES), this method * just returns super's implementation. During creation, every time the * "corner" is moved, a new line segment is added to the Scribble and * the bounding box is expanded if necessary. */ { float *p; if (gFlags.initialized) { return [super moveCorner:corner to:point constrain:flag]; } if (!(point->x - lastPoint.x || point->y - lastPoint.y)) return corner; length++; if (!(length % CHUNK_SIZE)) [self allocateChunk]; p = points + (length << 1); *p++ = point->x - lastPoint.x; *p = point->y - lastPoint.y; lastPoint = *point; bbox[2] = MAX(point->x, bbox[2]); bbox[0] = MIN(point->x, bbox[0]); bbox[3] = MAX(point->y, bbox[3]); bbox[1] = MIN(point->y, bbox[1]); bounds.origin.x = bbox[0]; bounds.origin.y = bbox[1]; bounds.size.width = bbox[2] - bbox[0]; bounds.size.height = bbox[3] - bbox[1]; return corner; } - (BOOL)create:(NXEvent *)event in:view /* * Before creating, an initial chunk is initialized, and the userPathOps * are initialized. The lastPoint is also remembered as the start point. * After the Scribble is created, the initialized flag is set. */ { NXPoint p; [self allocateChunk]; userPathOps[0] = dps_moveto; p = event->location; [view convertPoint:&p fromView:nil]; [view grid:&p]; points[0] = p.x; points[1] = p.y; lastPoint = p; bbox[0] = bbox[2] = p.x; bbox[1] = bbox[3] = p.y; bounds.origin = p; bounds.size.width = bounds.size.height = 0.0; if ([super create:event in:view]) { gFlags.initialized = YES; return YES; } return NO; } - draw /* * The Scribble is drawn simply by scaling appropriately from its * initial bounding box and drawing the user path. */ { NXCoord x, y; NXPoint p1, p2; int i, count, coords; float angle, sx, sy, tx, ty; if (bounds.size.width < 1.0 || bounds.size.height < 1.0) return self; if (length && (bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) { sx = bounds.size.width / (bbox[2] - bbox[0]); sy = bounds.size.height / (bbox[3] - bbox[1]); tx = (bounds.origin.x + ((points[0]-bbox[0]) / (bbox[2]-bbox[0]) * bounds.size.width)) - points[0] * sx; ty = (bounds.origin.y + ((points[1]-bbox[1]) / (bbox[3]-bbox[1]) * bounds.size.height)) - points[1] * sy; if (gFlags.arrow && ![self fill] && (sx != 1.0 || sy != 1.0 || tx || ty)) { PSgsave(); } if ([self fill]) { PSgsave(); PStranslate(tx, ty); PSscale(sx, sy); [self setFillColor]; DPSDoUserPath(points, (length + 1) << 1, dps_float, userPathOps, length + 1, bbox, gFlags.eofill ? dps_ueofill : dps_ufill); PSgrestore(); } if (!gFlags.nooutline) { PStranslate(tx, ty); PSscale(sx, sy); [self setLineColor]; DPSDoUserPath(points, (length + 1) << 1, dps_float, userPathOps, length + 1, bbox, dps_ustroke); } if (gFlags.arrow && ![self fill]) { if (sx != 1.0 || sy != 1.0 || tx || ty) { PSgrestore(); [self setLineColor]; } if (gFlags.arrow != ARROW_AT_END) { i = 0; p1.x = points[i++]; p1.y = points[i++]; p2 = p1; p2.x += points[i++]; p2.y += points[i++]; count = length - 1; while (hypot((p1.x-p2.x)*sx,(p1.y-p2.y)*sy) < 7.0 && count--) { p2.x += points[i++]; p2.y += points[i++]; } angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx); angle = (angle / 3.1415) * 180.0; x = bounds.origin.x + (p1.x - bbox[0]) * sx; y = bounds.origin.y + (p1.y - bbox[1]) * sy; PSArrow(x, y, angle); } if (gFlags.arrow != ARROW_AT_START) { i = 0; coords = (length + 1) << 1; p1.x = points[i++]; p1.y = points[i++]; while (i < coords) { p1.x += points[i++]; p1.y += points[i++]; } p2 = p1; i = coords; p2.y -= points[--i]; p2.x -= points[--i]; count = length - 1; while (hypot((p2.x-p1.x)*sx,(p2.y-p1.y)*sy) < 7.0 && count--) { p2.y -= points[--i]; p2.x -= points[--i]; } angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx); angle = (angle / 3.1415) * 180.0; x = bounds.origin.x + (p1.x - bbox[0]) * sx; y = bounds.origin.y + (p1.y - bbox[1]) * sy; PSArrow(x, y, angle); } } } return self; } - write:(NXTypedStream *)stream /* * The Scribble is written out by writing its length (in segments), its * bounding box, and all the points. */ { int i, numFloats; [super write:stream]; NXWriteTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]); numFloats = (length + 1) << 1; for (i = 0; i < numFloats; i++) { NXWriteTypes(stream, "f", &points[i]); } return self; } - read:(NXTypedStream *)stream { int i; float *p; [super read:stream]; NXReadTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]); NX_ZONEMALLOC([self zone], points, float, (length + 1) << 1); NX_ZONEMALLOC([self zone], userPathOps, char, length + 1); p = points; for (i = 0; i <= length; i++) { NXReadTypes(stream, "f", p++); NXReadTypes(stream, "f", p++); userPathOps[i] = dps_rlineto; } userPathOps[0] = dps_moveto; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.