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.