This is Polygon.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("Polygon", NULL, "Name of the tool that draws polygons, i.e., the %s of the New %s operation.") */ @implementation Polygon + initialize /* * This bumps the class version so that we can compatibly read * old Graphic objects out of an archive. */ { [Polygon setVersion:1]; return self; } + cursor /* * The cursor inherited from Scribble is a pencil. * That's not very appropriate, so CrossCursor is used instead. */ { return CrossCursor; } static void getRectFromBBox(NXRect *r, float x1, float y1, float x2, float y2) /* * Takes two points (x1, y1) and (x2, y2) and updates the r rect to * equal that bounding box. */ { r->size.width = x1 - x2; r->size.height = y1 - y2; if (r->size.width < 0.0) { r->origin.x = x2 + r->size.width; r->size.width = 0.0 - r->size.width; } else r->origin.x = x2; if (r->size.height < 0.0) { r->origin.y = y2 + r->size.height; r->size.height = 0.0 - r->size.height; } else r->origin.y = y2; } /* * This class probably is probably not implemented in the optimal way, * but it shows how an existing implementation (i.e. Scribble) can be * used to implement some other object. * * This method creates a polygon. The user must drag out each segment of * the polygon clicking to make a corner, finally ending with a double click. * * Start by getting the starting point of the polygon from the mouse down * event passed in the event parameter (if the ALT key is not down, then we * will close the path even if the user does not explicitly do so). * * Next, we initialize a chunk of space for the points to be stored in * and initialize point[0] and point[1] to be the starting point (since the * first thing in the userpath is a moveto). We also initialize our bounding * box to contain only that point. * * p represents the last point the user moved the mouse to. We initialize it * to start before entering the tracking loop. * * Inside the loop, last represents the last point the user confirmed (by * clicking) as opposed to p, the last point the user moved to. We update * last every time we start the segment tracking loop (the inner, * while (event->type != NX_MOUSEUP) loop). * * In the segment tracking loop, r represents the rectangle which must be * redrawn to get rid of the last time we drew the segment we are currently * tracking. After we [view drawSelf:&r :1] to clear out the last segment, * we recalculate the value of r for the next time around the loop. Finally, * we draw ourselves (i.e. all the other segments besides the one we are * currently tracking) and then draw the segment we are currently tracking. * * After tracking the segment, we check to see if we are done. * We are finished if any of the following are true: * 1. The last segment the user created was smaller than a gridSpacing. * 2. The user clicked on the starting point (thereby closing the path). * 3. The mouse down is outside the view's bounds. * 4. A kit defined or system defined event comes through. * * If we are not done (or we need to close the path), then we store the * new point pair into points (reallocating our points * and userPathOps arrays if we are out of room). We then update our bounding * box to reflect the new point and update our bounds to equal our bounding * box. If we aren't done, we look for the next mouse down to begin the * tracking of another segment. * * After we are finished with all segments, we check to be sure that we have * at least two segments (one segment is a line, not a polygon). If the * path is closed, then we need at least three segments. If we have the * requisite number of segments, then we reallocate our arrays to fit exactly * our number of points and return YES. Otherwise, we free the storage of * those arrays and clean up any drawing we did and return NO. */ #define POLYGON_MASK (NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK) #define END_POLYGON_MASK (NX_KITDEFINEDMASK|NX_MOUSEDOWNMASK|NX_SYSDEFINEDMASK) - (BOOL)create:(NXEvent *)event in:view { float *pptr; NXRect r, viewBounds; NXPoint start, last, p; Window *window = [view window]; BOOL closepath, done = NO, resend = NO; float grid = (float)[view gridSpacing]; int windowNum = event->window, arrow = 0; if (![view gridIsEnabled]) grid = 1.0; gFlags.initialized = YES; if (gFlags.arrow && gFlags.arrow != ARROW_AT_START) { arrow = gFlags.arrow; gFlags.arrow = (gFlags.arrow == ARROW_AT_END) ? 0 : ARROW_AT_START; } start = event->location; [view convertPoint:&start fromView:nil]; [view grid:&start]; [view getVisibleRect:&viewBounds]; closepath = (event->flags & NX_ALTERNATEMASK) ? NO : YES; length = 0; [self allocateChunk]; pptr = points; *pptr++ = bbox[0] = bbox[2] = start.x; *pptr++ = bbox[1] = bbox[3] = start.y; userPathOps[0] = dps_moveto; [view lockFocus]; [self setLineColor]; PSsetlinewidth(linewidth); p = start; event = [NXApp getNextEvent:POLYGON_MASK]; while (!done) { last = p; if (event->type == NX_MOUSEDOWN) { if (event->data.mouse.click > 1) { done = YES; [NXApp getNextEvent:NX_MOUSEUPMASK]; } else if (event->window != windowNum) { done = YES; resend = YES; } else { p = event->location; [view convertPoint:&p fromView:nil]; done = !NXMouseInRect(&p, &viewBounds, NO); resend = YES; } } else if (event->type == NX_KITDEFINED || event->type == NX_SYSDEFINED) { done = YES; resend = YES; } if (!done) { while (event->type != NX_MOUSEUP) { p = event->location; [view convertPoint:&p fromView:nil]; [view grid:&p]; [view drawSelf:&r :1]; getRectFromBBox(&r, p.x, p.y, last.x, last.y); [view scrollPointToVisible:&p]; NXInsetRect(&r, -2.0, -2.0); [self draw]; PSmoveto(last.x, last.y); PSlineto(p.x, p.y); PSstroke(); [window flushWindow]; event = [NXApp getNextEvent:POLYGON_MASK]; } if (fabs(p.x-start.x) <= grid && fabs(p.y-start.y) <= grid) { done = YES; closepath = YES; } } if (!done || (closepath && length > 1)) { if (done) p = start; length++; if (!(length % CHUNK_SIZE)) [self allocateChunk]; *pptr++ = p.x - last.x; *pptr++ = p.y - last.y; if (p.x < bbox[0]) bbox[0] = p.x; if (p.x > bbox[2]) bbox[2] = p.x; if (p.y < bbox[1]) bbox[1] = p.y; if (p.y > bbox[3]) bbox[3] = p.y; getRectFromBBox(&bounds, bbox[0], bbox[1], bbox[2], bbox[3]); if (!done) event = [NXApp getNextEvent:END_POLYGON_MASK]; } } [view unlockFocus]; if (resend) DPSPostEvent(event, 1); if (arrow) gFlags.arrow = arrow; if (length > (closepath ? 2 : 1)) { points = NX_ZONEREALLOC([self zone], points, float, (length+1) << 1); userPathOps = NX_ZONEREALLOC([self zone], userPathOps, char, length+1); return YES; } else { NX_FREE(points); points = NULL; NX_FREE(userPathOps); userPathOps = NULL; [view drawSelf:[self getExtendedBounds:&r] :1]; return NO; } } - (Graphic *)colorAcceptorAt:(const NXPoint *)point { if ([self hit:point]) return self; return nil; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.