home *** CD-ROM | disk | FTP | other *** search
/ OpenStep 4.2J (Developer) / os42jdev.iso / NextDeveloper / Examples / AppKit / Draw / Polygon.m < prev    next >
Text File  |  1996-04-11  |  8KB  |  223 lines

  1. #import "draw.h"
  2.  
  3. /*
  4.  * This line is just a stub to get genstrings to generate
  5.  * a .strings file entry for the name of this type of Graphic.
  6.  * The name is used in the Undo New <Whatever> menu item.
  7.  *
  8.  * NSLocalString("Polygon", NULL, "Name of the tool that draws polygons, i.e., the %@ of the New %@ operation.")
  9.  */
  10.  
  11. @implementation Polygon
  12.  
  13. + (NSCursor *)cursor
  14. /*
  15.  * The cursor inherited from Scribble is a pencil.
  16.  * That's not very appropriate, so CrossCursor is used instead.
  17.  */
  18. {
  19.     return [Graphic cursor];
  20. }
  21.  
  22. static NSRect NSRectFromBBox(float x1, float y1, float x2, float y2)
  23. /*
  24.  * Takes two points (x1, y1) and (x2, y2) and updates the r rect to
  25.  * equal that bounding box.
  26.  */
  27. {
  28.     NSRect r;
  29.  
  30.     r.size.width = x1 - x2;
  31.     r.size.height = y1 - y2;
  32.     if (r.size.width < 0.0) {
  33.     r.origin.x = x2 + r.size.width;
  34.     r.size.width = 0.0 - r.size.width;
  35.     } else r.origin.x = x2;
  36.     if (r.size.height < 0.0) {
  37.     r.origin.y = y2 + r.size.height;
  38.     r.size.height = 0.0 - r.size.height;
  39.     } else r.origin.y = y2;
  40.  
  41.     return r;
  42. }
  43.  
  44. /*
  45.  * This class probably is probably not implemented in the optimal way,
  46.  * but it shows how an existing implementation (i.e. Scribble) can be
  47.  * used to implement some other object.
  48.  *
  49.  * This method creates a polygon.  The user must drag out each segment of
  50.  * the polygon clicking to make a corner, finally ending with a double click.
  51.  *
  52.  * Start by getting the starting point of the polygon from the mouse down
  53.  * event passed in the event parameter (if the ALT key is not down, then we
  54.  * will close the path even if the user does not explicitly do so).
  55.  *
  56.  * Next, we initialize a chunk of space for the points to be stored in
  57.  * and initialize point[0] and point[1] to be the starting point (since the
  58.  * first thing in the userpath is a moveto).  We also initialize our bounding
  59.  * box to contain only that point.
  60.  *
  61.  * p represents the last point the user moved the mouse to.  We initialize it
  62.  * to start before entering the tracking loop.
  63.  *
  64.  * Inside the loop, last represents the last point the user confirmed (by
  65.  * clicking) as opposed to p, the last point the user moved to.  We update
  66.  * last every time we start the segment tracking loop (the inner,
  67.  * while (event->type != NSMouseUp) loop).
  68.  *
  69.  * In the segment tracking loop, r represents the rectangle which must be
  70.  * redrawn to get rid of the last time we drew the segment we are currently
  71.  * tracking.  After we [view drawSelf:&r :1] to clear out the last segment,
  72.  * we recalculate the value of r for the next time around the loop.  Finally,
  73.  * we draw ourselves (i.e. all the other segments besides the one we are
  74.  * currently tracking) and then draw the segment we are currently tracking.
  75.  *
  76.  * After tracking the segment, we check to see if we are done.
  77.  * We are finished if any of the following are true:
  78.  *    1. The last segment the user created was smaller than a gridSpacing.
  79.  *    2. The user clicked on the starting point (thereby closing the path).
  80.  *    3. The mouse down is outside the view's bounds.
  81.  *    4. A kit defined or system defined event comes through.
  82.  *
  83.  * If we are not done (or we need to close the path), then we store the
  84.  * new point pair into points (reallocating our points
  85.  * and userPathOps arrays if we are out of room).  We then update our bounding
  86.  * box to reflect the new point and update our bounds to equal our bounding
  87.  * box.  If we aren't done, we look for the next mouse down to begin the
  88.  * tracking of another segment.
  89.  *
  90.  * After we are finished with all segments, we check to be sure that we have
  91.  * at least two segments (one segment is a line, not a polygon).  If the
  92.  * path is closed, then we need at least three segments.  If we have the
  93.  * requisite number of segments, then we reallocate our arrays to fit exactly
  94.  * our number of points and return YES.  Otherwise, we free the storage of
  95.  * those arrays and clean up any drawing we did and return NO.
  96.  */
  97.  
  98. #define POLYGON_MASK (NSLeftMouseDraggedMask|NSLeftMouseUpMask)
  99. #define END_POLYGON_MASK (NSAppKitDefinedMask|NSLeftMouseDownMask|NSApplicationDefinedMask)
  100.  
  101. - (BOOL)create:(NSEvent *)event in:view
  102. {
  103.     float *pptr;
  104.     NSRect viewBounds;
  105.     NSPoint start, last, p;
  106.     NSWindow *window = [view window];
  107.     BOOL closepath, done = NO, resend = NO;
  108.     float grid = (float)[view gridSpacing];
  109.     int windowNum = [event windowNumber], arrow = 0;
  110.  
  111.     if (![view gridIsEnabled]) grid = 1.0;
  112.  
  113.     gFlags.initialized = YES;
  114.     if (gFlags.arrow && gFlags.arrow != ARROW_AT_START) {
  115.     arrow = gFlags.arrow;
  116.     gFlags.arrow = (gFlags.arrow == ARROW_AT_END) ? 0 : ARROW_AT_START;
  117.     }
  118.  
  119.     start = [event locationInWindow];
  120.     start = [view convertPoint:start fromView:nil];
  121.     start = [view grid:start];
  122.  
  123.     viewBounds = [view visibleRect];
  124.  
  125.     closepath = ([event modifierFlags] & NSAlternateKeyMask) ? NO : YES;
  126.  
  127.     length = 0;
  128.     [self allocateChunk];
  129.     pptr = points;
  130.     *pptr++ = bbox[0] = bbox[2] = start.x;
  131.     *pptr++ = bbox[1] = bbox[3] = start.y;
  132.     userPathOps[0] = dps_moveto;
  133.  
  134.     [view lockFocus];
  135.  
  136.     [self setLineColor];
  137.     PSsetlinewidth(linewidth);
  138.  
  139.     p = start;
  140.     event = [window nextEventMatchingMask:POLYGON_MASK];
  141.     while (!done) {
  142.     last = p;
  143.     if ([event type] == NSLeftMouseDown) {
  144.         if ([event clickCount] > 1) {
  145.         done = YES;
  146.         [window nextEventMatchingMask:NSLeftMouseUpMask];
  147.         } else if ([event windowNumber] != windowNum) {
  148.         done = YES;
  149.         resend = YES;
  150.         } else {
  151.         p = [event locationInWindow];
  152.         p = [view convertPoint:p fromView:nil];
  153.         if (!NSMouseInRect(p, viewBounds, NO)) {
  154.                     done = YES;
  155.             resend = YES;
  156.         }
  157.         }
  158.     } else if ([event type] == NSAppKitDefined || [event type] == NSSystemDefined) {
  159.         done = YES;
  160.         resend = YES;
  161.     }
  162.     if (!done) {
  163.         NSRect aRect = NSZeroRect;
  164.         while ([event type] != NSLeftMouseUp) {
  165.         p = [event locationInWindow];
  166.         p = [view convertPoint:p fromView:nil];
  167.         p = [view grid:p];
  168.         [view drawRect:aRect];
  169.         aRect = NSRectFromBBox(p.x, p.y, last.x, last.y);
  170.         [view scrollPointToVisible:p];
  171.         aRect = NSInsetRect(aRect, -2.0, -2.0);
  172.         [self draw];
  173.         PSmoveto(last.x, last.y);
  174.         PSlineto(p.x, p.y);
  175.         PSstroke();
  176.         [window flushWindow];
  177.         event = [window nextEventMatchingMask:POLYGON_MASK];
  178.         }
  179.         if (fabs(p.x-start.x) <= grid && fabs(p.y-start.y) <= grid) {
  180.         done = YES;
  181.         closepath = YES;
  182.         }
  183.     }
  184.     if (!done || (closepath && length > 1)) {
  185.         if (done) p = start;
  186.         length++;
  187.         if (!(length % CHUNK_SIZE)) [self allocateChunk];
  188.         *pptr++ = p.x - last.x;
  189.         *pptr++ = p.y - last.y;
  190.         if (p.x < bbox[0]) bbox[0] = p.x;
  191.         if (p.x > bbox[2]) bbox[2] = p.x;
  192.         if (p.y < bbox[1]) bbox[1] = p.y;
  193.         if (p.y > bbox[3]) bbox[3] = p.y;
  194.         bounds = NSRectFromBBox(bbox[0], bbox[1], bbox[2], bbox[3]);
  195.         if (!done) event = [window nextEventMatchingMask:END_POLYGON_MASK];
  196.     }
  197.     }
  198.  
  199.     [view unlockFocus];
  200.  
  201.     if (resend) [NSApp postEvent:event atStart:YES];
  202.     if (arrow) gFlags.arrow = arrow;
  203.  
  204.     if (length > (closepath ? 2 : 1)) {
  205.     points = points = NSZoneRealloc([self zone], points, ((length+1) << 1) * sizeof(float));
  206.     userPathOps = userPathOps = NSZoneRealloc([self zone], userPathOps, (length+1) * sizeof(char));
  207.     return YES;
  208.     } else {
  209.     NSZoneFree([self zone], points); points = NULL;
  210.     NSZoneFree([self zone], userPathOps); userPathOps = NULL;
  211.     if (length) [view drawRect:[self extendedBounds]]; // clean up aborted Polygon
  212.     return NO;
  213.     }
  214. }
  215.  
  216. - (Graphic *)colorAcceptorAt:(NSPoint)point
  217. {
  218.     return [self hit:point] ? self : nil;
  219. }
  220.  
  221. @end
  222.  
  223.