This is ConnectorView.m in view mode; [Download] [Up]
/* * Copyright, 1991, The Regents of the University of * California. This software was produced under a U. S. * Government contract (W-7405-ENG-36) by the Los Alamos * National Laboratory, which is operated by the * University of California for the U. S. Department of * Energy. The U. S. Government is licensed to use, * reproduce, and distribute this software. Permission * is granted to the public to copy and use this software * without charge, provided that this Notice and any statement * of authorship are reproduced on all copies. Neither the * Government nor the University makes any warranty, express * or implied, or assumes any liability or responsibility for * the use of this software. */ /* * ConnectorView.m * By Bill Edney, Los Alamos National Laboratory */ #import <math.h> #import <objc/List.h> #import <objc/Storage.h> #import <stdlib.h> #import<dpsclient/psops.h> #import<dpsclient/wraps.h> #import <appkit/Window.h> #import <appkit/Panel.h> #import <appkit/Application.h> #import <appkit/publicWraps.h> #import <appkit/nextstd.h> #import "BlackenView.h" #import "ConnectorView.h" @implementation ConnectorView - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; connectionWidth = 2.0; return self; } - awake { /* Allow our superclass to awake */ [super awake]; /* Create a list to hold our local list of rectangles */ viewRectList = [[Storage alloc] initCount:0 elementSize:sizeof(ViewRectPair) description:"{@ffff}"]; /* THIS IS FOR USING THIS CODE IN THE IB PALETTE ONLY */ /* Take this out for real applications where you are registering meaningful rects */ [self registerRect:&bounds forView:self]; return self; } /* Add the view-rect pair to our own, local, list */ /* NOTE: Rects MUST be registered in their own local view's coordinates */ - registerRect:(NXRect *)rect forView:view { ViewRectPair *newViewRect; newViewRect = (ViewRectPair *)malloc(sizeof(ViewRectPair)); newViewRect->view = view; newViewRect->rect = *rect; [viewRectList addElement:newViewRect]; return self; } - buildGlobalRectList { ViewRectPair *oldPair; ViewRectPair *newPair; id winList, theWindow; int winNum; id viewList, theView; int viewNum; id hitList; int hitNum; int i,j,k; i = j = k = 0; winNum = viewNum = hitNum = 0; winList = [NXApp windowList]; winNum = [winList count]; if (winNum > 0) { for (i = 0;i <= winNum-1;i++) { theWindow = [winList objectAt:i]; viewList = [[theWindow contentView] subviews]; viewNum = [viewList count]; if (viewNum > 0) { for (j = 0;j <= viewNum-1;j++) { theView = [viewList objectAt:j]; if ([theView respondsTo:@selector(viewRectList)]) { hitList = [theView viewRectList]; hitNum = [hitList count]; if (hitNum > 0) { for (k = 0; k <= hitNum-1;k++) { oldPair = (ViewRectPair *)[hitList elementAt:k]; newPair = (ViewRectPair *)malloc(sizeof(ViewRectPair)); newPair->view = theView; newPair->rect = oldPair->rect; [globRectList addElement:newPair]; } oldPair = newPair = NULL; } } } } } } return self; } - (ViewRectPair *)checkForAcceptRect:(NXPoint)thePoint { int numViewRects; ViewRectPair *vrPair; int i; NXPoint globPoint; /* Get the number of elements in our globRectList */ numViewRects = [globRectList count]; globPoint = thePoint; for (i = 0; i<= numViewRects-1;i++) { /* Get the element and convert the global point into a local view one */ vrPair = (ViewRectPair *)[globRectList elementAt:i]; [[(vrPair->view) window] convertScreenToBase:&thePoint]; [(vrPair->view) convertPoint:&thePoint fromView:nil]; if (NXPointInRect(&thePoint, &(vrPair->rect))) return (vrPair); /* Reset the point to the global point for the next test */ thePoint = globPoint; } return NULL; } - drawSelf:(NXRect *)r :(int)count { PSsetgray(NX_WHITE); NXRectFill(&bounds); PSsetgray(NX_BLACK); NXFrameRect(&bounds); return self; } - (float)connectionWidth { return connectionWidth; } - setConnectionWidth:(float)theWidth { connectionWidth = theWidth; return self; } - (id)viewRectList { return viewRectList; } - (BOOL)acceptsFirstMouse /* We want events that caused our window to become key window */ { return YES; } - mouseDown:(NXEvent *)theEvent { NXRect tempContRect; id vertView, horizView,mDownView; NXPoint mouseDownPoint; float mDownOffset = 0.0; /* Allocate and build the global rectangle list for all the hit rectangles in all the views in all the windows of the application */ globRectList = [[Storage alloc] initCount:10 elementSize:sizeof(ViewRectPair) description:"{@ffff}"]; [self buildGlobalRectList]; /* Get the point at which the mouse went down */ mouseDownPoint = theEvent->location; /* Convert the point from window's base coord system to screen coord system */ [[self window] convertBaseToScreen:&mouseDownPoint]; /* Check our GLOBAL rectangle list and set our startAcceptor */ startAcceptor = [self checkForAcceptRect:mouseDownPoint]; /* If there was a "hit rect" beneath our mouseDown point AND the user was holding down the control key, go into our drag loop */ if ((startAcceptor) && (theEvent->flags & NX_CONTROLMASK)) { /* Highlight the rect we went down in */ [self mouseWentDown:startAcceptor]; /* Set up the vertical window */ NXSetRect(&vertWinRect,mouseDownPoint.x,mouseDownPoint.y, connectionWidth,0.0); [Window getContentRect:&tempContRect forFrameRect:&vertWinRect style:NX_PLAINSTYLE]; vertWindow = [[Window alloc] initContent:&tempContRect style:NX_PLAINSTYLE /* We don't want a title bar */ backing:NX_BUFFERED buttonMask:0 defer:NO]; vertView = [[BlackenView alloc] initFrame:&vertWinRect]; [vertWindow setContentView:vertView]; /* We don't have any overlapping subviews in the window, so we can use optimized drawing */ [vertWindow useOptimizedDrawing:YES]; /* Set up the horizontal window - same initialization as vertical */ NXSetRect(&horizWinRect,mouseDownPoint.x,mouseDownPoint.y, 0.0, connectionWidth); [Window getContentRect:&tempContRect forFrameRect:&horizWinRect style:NX_PLAINSTYLE]; horizWindow = [[Window alloc] initContent:&tempContRect style:NX_PLAINSTYLE backing:NX_BUFFERED buttonMask:0 defer:NO]; horizView = [[BlackenView alloc] initFrame:&horizWinRect]; [horizWindow setContentView:horizView]; [horizWindow useOptimizedDrawing:YES]; /* Calculate mDownOffset to handle centering the box around the point clicked */ mDownOffset = connectionWidth * 3.0; /* Set up the box window */ NXSetRect(&mDownRect,mouseDownPoint.x - connectionWidth, mouseDownPoint.y - connectionWidth, mDownOffset, mDownOffset); [Window getContentRect:&tempContRect forFrameRect:&mDownRect style:NX_PLAINSTYLE]; mDownWindow = [[Window alloc] initContent:&mDownRect style:NX_PLAINSTYLE backing:NX_RETAINED buttonMask:0 defer:NO]; mDownView = [[BlackenView alloc] initFrame:&mDownRect]; [mDownWindow setContentView:mDownView]; /* Now, order the windows in the front of every other window */ [vertWindow orderFront:NULL]; [horizWindow orderFront:NULL]; [mDownWindow orderFront:NULL]; /* Display them */ [vertWindow display]; [horizWindow display]; [mDownWindow display]; /* Start dragging 'em around! */ [self dragConnectionUsingStartPoint:mouseDownPoint]; } else /* Otherwise, pass on the event to the next responder */ [nextResponder mouseDown:theEvent]; return self; } - dragConnectionUsingStartPoint:(NXPoint)startPoint { int mask; NXEvent *event; BOOL moveHoriz = NO,moveVert = NO; NXPoint currentPoint; ViewRectPair *newAcceptor = NULL; ViewRectPair *oldAcceptor = NULL; /* get mouse-dragged events also (and save original mask for later) */ mask = [window addToEventMask:NX_MOUSEDRAGGEDMASK]; /* Make the current point equal to the starting point */ currentPoint = startPoint; do /* While we're dragging the mouse */ { /* Check to see if the mouse point is in the "start box" */ if (NXMouseInRect(¤tPoint,&mDownRect,NO)) { for (;;) /* Cycle through until the user comes out */ { if (!NXMouseInRect(¤tPoint,&mDownRect,NO)) { if ((currentPoint.y > (mDownRect.size.height + mDownRect.origin.y)) || (currentPoint.y < mDownRect.origin.y)) { /* She came out vertically, move the horizontal */ moveVert = NO; moveHoriz = YES; break; /* Break out of this bloody loop */ } else if ((currentPoint.x > (mDownRect.size.width + mDownRect.origin.x)) || (currentPoint.x < mDownRect.origin.x)) { /* She came out horizontally, move the vertical */ moveVert = YES; moveHoriz = NO; break; /* Break out of this bloody loop */ } } /* Get the next event */ event = [NXApp getNextEvent:NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK]; currentPoint = event->location; [[self window] convertBaseToScreen:¤tPoint]; } } if (moveVert == YES) /* We're moving the vertical, calc both windows origins */ { vertWinRect.origin.x = currentPoint.x; horizWinRect.origin.y = startPoint.y; } if (moveHoriz == YES) /* We're moving the horizontal, calc both windows origins */ { vertWinRect.origin.x = startPoint.x; horizWinRect.origin.y = currentPoint.y; } /* Now, calculate the other origins and the extents of the windows */ if (currentPoint.y > startPoint.y) { vertWinRect.origin.y = startPoint.y; vertWinRect.size.height = currentPoint.y - startPoint.y + connectionWidth; } else if (currentPoint.y < startPoint.y) { vertWinRect.origin.y = currentPoint.y; vertWinRect.size.height = startPoint.y - currentPoint.y; } if (currentPoint.x > startPoint.x) { horizWinRect.origin.x = startPoint.x; horizWinRect.size.width = currentPoint.x - startPoint.x + connectionWidth; } else if (currentPoint.x < startPoint.x) { horizWinRect.origin.x = currentPoint.x; horizWinRect.size.width = startPoint.x - currentPoint.x; } [vertWindow disableFlushWindow]; /* Does this stuff really improve performance? */ [horizWindow disableFlushWindow]; /* I don't know */ /* Place and display our windows!!! */ [vertWindow placeWindowAndDisplay:&vertWinRect]; [horizWindow placeWindowAndDisplay:&horizWinRect]; [vertWindow reenableFlushWindow]; /* More supposedly performance improving stuff */ [horizWindow reenableFlushWindow]; /* Hard to tell with an '040 board */ [vertWindow flushWindow]; [horizWindow flushWindow]; /* See if the mouse point is in another acceptor */ newAcceptor = [self checkForAcceptRect:currentPoint]; /* If there isn't a newAcceptor, but there was an old one, tell the old one we left it */ if ((newAcceptor == NULL) && (oldAcceptor)) [self mouseWentOut:oldAcceptor]; if (newAcceptor) /* If there is a newAcceptor, tell it we came into it */ [self mouseCameIn:newAcceptor]; oldAcceptor = newAcceptor; /* Make the old acceptor equal to the new one */ /* Get the next event to process */ event = [NXApp getNextEvent:NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK]; currentPoint = event->location; [[self window] convertBaseToScreen:¤tPoint]; } while (event->type == NX_MOUSEDRAGGED); /* No longer need mouse dragged events, so reset the event mask */ [window setEventMask:mask]; /* If oldAcceptor exists, tell ourself that the user let go of the mouse */ if (oldAcceptor) { /* Tell ourselves that the mouse was dropped in an acceptor */ [self mouseDropped:startAcceptor :oldAcceptor]; /* Hide and free the connection windows */ [[vertWindow orderOut:self] free]; [[horizWindow orderOut:self] free]; [[mDownWindow orderOut:self] free]; [oldAcceptor->view display]; /* Call the acceptor's "drawSelf" to paint over its boxes */ /* Free our GLOBAL rectangle list */ [globRectList free]; } else { /* Hide and free the connection windows */ [[vertWindow orderOut:self] free]; [[horizWindow orderOut:self] free]; [[mDownWindow orderOut:self] free]; /* Free our GLOBAL rectangle list */ [globRectList free]; } [self display]; /* Call our "drawSelf" to paint over our boxes */ startAcceptor = NULL; /* We're done with the startAcceptor, so set it to null */ return self; } - mouseWentDown:(ViewRectPair *)acceptor { /* The mouse went down, highlight the starting acceptor */ [acceptor->view lockFocus]; PSsetgray(NX_BLACK); NXFrameRectWithWidth(&(acceptor->rect),connectionWidth); [[acceptor->view window] flushWindow]; [acceptor->view unlockFocus]; return self; } - mouseCameIn:(ViewRectPair *)acceptor { /* We don't the starting ViewRectPair to highlight */ if ((acceptor == startAcceptor) && (NXEqualRect(&(acceptor->rect),&(startAcceptor->rect)))) return self; /* Lock focus on the view and highlight it */ [acceptor->view lockFocus]; PSsetgray(NX_BLACK); NXFrameRectWithWidth(&(acceptor->rect),connectionWidth); [[acceptor->view window] flushWindow]; [acceptor->view unlockFocus]; return self; } - mouseWentOut:(ViewRectPair *)acceptor { /* We don't the starting ViewRectPair to unhighlight */ if ((acceptor == startAcceptor) && (NXEqualRect(&(acceptor->rect),&(startAcceptor->rect)))) return self; /* Unhighlight the view */ [acceptor->view display]; [[acceptor->view window] flushWindow]; return self; } - mouseDropped:(ViewRectPair *)fromAcceptor :(ViewRectPair *)toAcceptor { int result = 0; /* We don't the starting ViewRectPair to get its own connection */ if ((fromAcceptor == toAcceptor) && (NXEqualRect(&(fromAcceptor->rect),&(toAcceptor->rect)))) return self; /* Bring up an alert panel */ result = NXRunAlertPanel(NULL,"You just connected something","Yi doggie!",NULL,NULL); return 0; } - (const char *)inspectorName { /* Return the name for the IB inspector */ return "ConnectorViewInspector"; } /* We only read and write "connectionWidth" here. You might want to do others */ - read:(NXTypedStream*)stream { [super read:stream]; NXReadTypes(stream,"f", &connectionWidth); return self; } - write:(NXTypedStream*)stream { [super write:stream]; NXWriteTypes(stream,"f", &connectionWidth); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.