This is Tree.m in view mode; [Download] [Up]
#import <math.h> #import <appkit/workspaceRequest.h> #import <misckit/MiscString.h> #import "PSWEnding.h" #import "PSWUControl.h" #import "RTFDescription.h" #import "UndoManager.h" #import "Tree.h" //************************************************************************ // static c functions declaration // static functions for layouting static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl, NXCoord *low, NXCoord *loh); static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone); static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2, NXCoord b1,NXCoord b2); static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1, NXCoord x2,NXCoord y2, NXZone *zone); static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone); static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone); static void freePolyline(Polyline *aLine); static void partialFreePolyline(Polyline *aLine, Polyline *toEnd); // static functions for calculating path intersection static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4); // static variables for general use static float controlPts[8],controlX,controlY,officialFontSize; static char controlChars[5]; static char controlPrimChars[5]; static NXAtom fontname; static ButtonCell *attachCell, *soundCell; static Cell *zippedCell; static NXRect attachCellFrame, soundCellFrame, zippedCellFrame; static UPath *arrowEnding, *doubleEnding, *circleEnding; static UPath *parentArrowEnding, *parentDoubleEnding, *parentCircleEnding; // static variables for diagram2 streaming #import "DGVariables.h" @interface Tree(PrivateMethods) - attachParent:(NXCoord)h; - layoutLeaf; - (NXCoord)join; - makePath; - changeText; - changePos; - fillGPathParams; - calcWidthHeight; - internalCopyFromZone:(NXZone *)zone parent:aParent; - calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint; @end @implementation Tree //************************************************************************ // creating and destroying + initialize { int i; #ifndef FILTER NXRect dummy; id dummyId; #endif if(self == [Tree class]){ [Tree setVersion:1]; fontname = NXUniqueString("ControlPoints"); PSWDefineFont(fontname); officialFontSize = 8.0; controlPts[6] = 0; controlPts[7] = 0; controlChars[4] = 0; controlPrimChars[4] = 0; for(i = 0;i < 4;i++){ controlChars[i] = 'a'; controlPrimChars[i] = 'e'; } #ifndef FILTER dummyId = [NXImage findImageNamed:"NXLinkButton"]; if([dummyId lockFocus]) [dummyId unlockFocus]; dummyId = [NXImage findImageNamed:"NXLinkButtonH"]; if([dummyId lockFocus]) [dummyId unlockFocus]; dummyId = [NXImage findImageNamed:"zipped"]; if([dummyId lockFocus]) [dummyId unlockFocus]; dummyId = [NXImage findImageNamed:"soundMarker"]; if([dummyId lockFocus]) [dummyId unlockFocus]; dummyId = [NXImage findImageNamed:"soundMarkerH"]; if([dummyId lockFocus]) [dummyId unlockFocus]; NXSetRect(&dummy, 0, 0, 100, 100); attachCell = [[ButtonCell alloc] initIconCell:"NXLinkButton"]; [attachCell setAltIcon:"NXLinkButtonH"]; [attachCell setBordered:NO]; [attachCell setHighlightsBy:NX_CONTENTS]; [attachCell setIconPosition:NX_ICONONLY]; [attachCell calcCellSize:&(attachCellFrame.size) inRect:&dummy]; zippedCell = [[Cell alloc] initIconCell:"zipped"]; [zippedCell setBordered:NO]; [zippedCell calcCellSize:&(zippedCellFrame.size) inRect:&dummy]; soundCell = [[ButtonCell alloc] initIconCell:"soundMarker"]; [soundCell setAltIcon:"soundMarkerH"]; [soundCell setBordered:NO]; [soundCell setHighlightsBy:NX_CONTENTS]; [soundCell setIconPosition:NX_ICONONLY]; [soundCell calcCellSize:&(soundCellFrame.size) inRect:&dummy]; #endif arrowEnding = [[UPath alloc] init]; [arrowEnding moveto:0 :0]; [arrowEnding rlineto:-10 :5]; [arrowEnding rlineto:3 :-5]; [arrowEnding rlineto:-3 :-5]; [arrowEnding closepath]; doubleEnding = [[UPath alloc] init]; [doubleEnding moveto:0 :0]; [doubleEnding rlineto:-5 :5]; [doubleEnding rlineto:2 :-5]; [doubleEnding rlineto:-7 :5]; [doubleEnding rlineto:2 :-5]; [doubleEnding rlineto:-2 :-5]; [doubleEnding rlineto:7 :5]; [doubleEnding rlineto:-2 :-5]; [doubleEnding closepath]; circleEnding = [[UPath alloc] init]; [circleEnding moveto:0 :0]; [circleEnding arc:-3 :0 :3 :0 :360]; parentArrowEnding = [[UPath alloc] init]; [parentArrowEnding moveto:0 :0]; [parentArrowEnding rlineto:10 :5]; [parentArrowEnding rlineto:-3 :-5]; [parentArrowEnding rlineto:3 :-5]; [parentArrowEnding closepath]; parentDoubleEnding = [[UPath alloc] init]; [parentDoubleEnding moveto:0 :0]; [parentDoubleEnding rlineto:5 :5]; [parentDoubleEnding rlineto:-2 :-5]; [parentDoubleEnding rlineto:7 :5]; [parentDoubleEnding rlineto:-2 :-5]; [parentDoubleEnding rlineto:2 :-5]; [parentDoubleEnding rlineto:-7 :5]; [parentDoubleEnding rlineto:2 :-5]; [parentDoubleEnding closepath]; parentCircleEnding = [[UPath alloc] init]; [parentCircleEnding moveto:6 :0]; [parentCircleEnding arc:3 :0 :3 :0 :360]; } return self; } - initLabel:(const char *)aLabel props:(Properties *)aProps { [super init]; contour.lower.head = contour.upper.head = NULL; contour.lower.tail = contour.upper.tail = NULL; parent = child = sibling = nil; delegate = nil; hasVerknuepfung = NO; verknuepfung = nil; hasSound = NO; geraeusch = nil; offset.x = offset.y = 0.0; pos.x = pos.y = 0.0; selected = NO; updateLayout = YES; if(aLabel) textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aLabel]; else textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aProps->defaultNodeName]; [textCell setBordered:NO]; [textCell setBackgroundTransparent:YES]; [textCell setTextColor:aProps->textColor]; [textCell setAlignment:NX_CENTERED]; nodeDescription = [[RTFDescription allocFromZone:[self zone]] init]; zipped = aProps->zipped; border = aProps->border; shadow = aProps->shadow; fill = aProps->fill; outline = aProps->outline; linkKind = aProps->linkKind; biegFactor = aProps->biegFactor; parentDistance = aProps->parentDistance; fillColor = aProps->fillColor; outlineColor = aProps->outlineColor; shadowColor = aProps->shadowColor; ending = aProps->ending; parentEnding = aProps->parentEnding; gpath = [[UPath allocFromZone:[self zone]] init]; pathKind = aProps->pathKind; deltaShadow.x = deltaShadow.y = 3; placer.rect[0] = placer.rect[1] = 6; placer.roundRect[0] = placer.roundRect[1] = 6; placer.roundRect[2] = 3; placer.ellipse[0] = 6; placer.ellipse[1] = 4; placer.rhomb[0] = 12; placer.rhomb[1] = 3; placer.hexagon[0] = 6; placer.hexagon[1] = 6; placer.hexagon[2] = 1; [self makePath]; return self; } - free { [nodeDescription free]; if(contour.lower.head) freePolyline(contour.lower.head); if(contour.upper.head) freePolyline(contour.upper.head); if(child) [child free]; if(sibling) [sibling free]; [gpath free]; if(verknuepfung) [verknuepfung free]; if(geraeusch) [geraeusch free]; [textCell free]; return [super free]; } - copyFromZone:(NXZone *)zone { Tree *theCopy; theCopy = [super copyFromZone:zone]; theCopy->contour.upper.head = contour.upper.tail = NULL; theCopy->contour.lower.head = contour.lower.tail = NULL; theCopy->updateLayout = YES; theCopy->parent = nil; theCopy->sibling = nil; if(child) theCopy->child = [child internalCopyFromZone:zone parent:theCopy]; else theCopy->child = nil; theCopy->selected = NO; theCopy->gpath = [gpath copyFromZone:zone]; theCopy->nodeDescription = [nodeDescription copyFromZone:zone]; theCopy->delegate = nil; if(verknuepfung) theCopy->verknuepfung = [verknuepfung copyFromZone:zone]; else theCopy->verknuepfung = nil; if(geraeusch) theCopy->geraeusch = [geraeusch copyFromZone:zone]; else theCopy->geraeusch = nil; theCopy->textCell = [textCell copyFromZone:zone]; return theCopy; } //************************************************************************ // layout and drawing - involved:(const NXRect *)aRect addTo:aList link:aPath { NXRect rect,dummyRect; NXPoint point,mp,parentPoint,childPoint; NXCoord dist; if(parent && parent->zipped) return self; [self getBounds:&rect]; if(aRect == NULL || NXIntersectsRect(&rect,aRect)) [aList addObject:self]; if(parent){ [parent linkPoint:&point]; [self linkPoint:&mp]; if(linkKind){ [parent linkParentPoint:&parentPoint]; [self linkChildPoint:&childPoint]; } dist = ABS(point.y - mp.y); if(dist > 0) NXSetRect(&dummyRect,MIN(mp.x,point.x) - 0.5,MIN(mp.y,point.y) - 0.5, ABS(point.x - mp.x) + 1,dist + 1); else NXSetRect(&dummyRect,MIN(mp.x,point.x),mp.y - 0.5, ABS(point.x - mp.x),1.0); if(aRect == NULL || NXIntersectsRect(aRect,&dummyRect)){ if(linkKind == 0){ [aPath moveto:point.x :point.y]; [aPath lineto:mp.x :mp.y]; } else { [aPath moveto:point.x :point.y]; [aPath lineto:parentPoint.x :parentPoint.y]; [aPath lineto:childPoint.x :childPoint.y]; [aPath lineto:mp.x :mp.y]; } } } if(child) [child involved:aRect addTo:aList link:aPath]; if(sibling) [sibling involved:aRect addTo:aList link:aPath]; return self; } - getBounds:(NXRect *)rect { NXRect dummy; NXCoord dxy, dwh; NXSetRect(rect,pos.x,pos.y,width,height); if(selected){ dxy = MAX(5, linewidth); dwh = MAX(10, linewidth); rect->origin.x -= dxy; rect->origin.y -= dxy; rect->size.width += dwh; rect->size.height += dwh; } if(shadow){ rect->size.width += deltaShadow.x; rect->size.height += deltaShadow.y; } if((ending && parent) || (parentEnding && child)){ NXInsetRect(rect, -15, -15); } if(hasVerknuepfung){ NXSetRect(&dummy, pos.x + 23, pos.y - linewidth - attachCellFrame.size.height - 1, attachCellFrame.size.width, attachCellFrame.size.height); NXUnionRect(&dummy, rect); } if(zipped){ NXSetRect(&dummy, pos.x + 7, pos.y - linewidth - zippedCellFrame.size.height - 1, zippedCellFrame.size.width, zippedCellFrame.size.height); NXUnionRect(&dummy, rect); } if(hasSound){ NXSetRect(&dummy, pos.x + 37, pos.y - linewidth - soundCellFrame.size.height - 1, soundCellFrame.size.width, soundCellFrame.size.height); NXUnionRect(&dummy, rect); } return self; } - getTreeBounds:(NXRect *)rect lowerWidth:(NXCoord *)lowW lowerHeight:(NXCoord *)lowH { NXPoint p1,p2; p1.x = pos.x - border; p1.y = pos.y - border; p2.x = pos.x - border; p2.y = pos.y + height + border; setRectFromPolygon(rect, &contour, &p1, &p2, lowW, lowH); NXInsetRect(rect,border - linewidth,border - linewidth); if(shadow){ rect->size.width += deltaShadow.x; rect->size.height += deltaShadow.y; } return self; } - hit:(HitPath *)hitUPath { BOOL didHit; id dummy; didHit = [hitUPath hitPathFill:gpath]; if(didHit) return self; else if(child){ dummy = [child hit:hitUPath]; if(dummy) return dummy; else if(sibling) return [sibling hit:hitUPath]; } else if(sibling) return [sibling hit:hitUPath]; return nil; } - hitAttachment:(const NXPoint *)aPoint :(BOOL *)theAttach { BOOL didHit; id dummy; NXRect dummyRect; *theAttach = NO; if(hasVerknuepfung){ NXSetRect(&dummyRect, pos.x + 23, pos.y - linewidth - attachCellFrame.size.height - 1, attachCellFrame.size.width, attachCellFrame.size.height); didHit = NXPointInRect(aPoint, &dummyRect); } else didHit = NO; if(didHit){ *theAttach = YES; return self; } if(hasSound){ NXSetRect(&dummyRect, pos.x + 37, pos.y - linewidth - soundCellFrame.size.height - 1, soundCellFrame.size.width, soundCellFrame.size.height); didHit = NXPointInRect(aPoint, &dummyRect); } else didHit = NO; if(didHit) return self; if(child){ dummy = [child hitAttachment:aPoint :theAttach]; if(dummy) return dummy; else if(sibling) return [sibling hitAttachment:aPoint :theAttach]; } else if(sibling) return [sibling hitAttachment:aPoint :theAttach]; return nil; } - activateAttachment:(NXEvent *)event inView:aView { NXPoint p; NXRect dummyRect; id result; if(!hasVerknuepfung) return self; attachCellFrame.origin.x = pos.x + 23; attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1; [attachCell highlight:&attachCellFrame inView:aView lit:YES]; [[aView window] flushWindow]; while(event->type != NX_MOUSEUP) event = [NXApp getNextEvent:NX_MOUSEUPMASK]; p = event->location; [aView convertPoint:&p fromView:nil]; dummyRect = attachCellFrame; if(NXPointInRect(&p, &dummyRect)){ [[Application workspace] openFile:[verknuepfung sourceFilename]]; result = self; } else result = nil; [attachCell highlight:&attachCellFrame inView:aView lit:NO]; [[aView window] flushWindow]; return result; } - activateSound:(NXEvent *)event inView:aView { NXPoint p; NXRect dummyRect; id result; if(!hasSound) return self; soundCellFrame.origin.x = pos.x + 37; soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1; [soundCell highlight:&soundCellFrame inView:aView lit:YES]; [[aView window] flushWindow]; while(event->type != NX_MOUSEUP) event = [NXApp getNextEvent:NX_MOUSEUPMASK]; p = event->location; [aView convertPoint:&p fromView:nil]; dummyRect = soundCellFrame; if(NXPointInRect(&p, &dummyRect)){ [geraeusch play]; result = self; } else result = nil; [soundCell highlight:&soundCellFrame inView:aView lit:NO]; [[aView window] flushWindow]; return result; } - drawNodeShadow:(DrawState *)aState { NXPoint dummy; if(shadow){ if(!NXEqualColor(aState->color,shadowColor)){ aState->color = shadowColor; NXSetColor(shadowColor); } dummy = pos; pos.x += deltaShadow.x; pos.y += deltaShadow.y; [self changePos]; [gpath send:dps_ufill cached:YES]; pos = dummy; [self changePos]; } return self; } - drawNodeFill:(DrawState *)aState { if(fill){ if(!NXEqualColor(aState->color,fillColor)){ aState->color = fillColor; NXSetColor(fillColor); } [gpath send:dps_ufill cached:YES]; } return self; } - drawNodeEnding:(DrawState *)aState { NXPoint outPoint,iPoint; float rotAngle; Tree *ptr; if(ending != ENDING_NONE && parent){ if(linkKind) [self linkChildPoint:&outPoint]; else [parent linkPoint:&outPoint]; if(!NXEqualColor(aState->color,NX_COLORBLACK)){ aState->color = NX_COLORBLACK; NXSetColor(NX_COLORBLACK); } if(aState->linewidth != 0.15){ aState->linewidth = 0.15; PSsetlinewidth(0.15); } [self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint]; switch(ending){ case ENDING_ARROW: PSWEndingSolid(arrowEnding->bboxParams, arrowEnding->sizeParams + 4, arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_HOLLOW: PSWEndingHollow(arrowEnding->bboxParams, arrowEnding->sizeParams + 4, arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_DOUBLE: PSWEndingSolid(doubleEnding->bboxParams, doubleEnding->sizeParams + 4, doubleEnding->bboxOps, doubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_SOLID: PSWEndingSolid(circleEnding->bboxParams, circleEnding->sizeParams + 4, circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_CIRCLE: PSWEndingHollow(circleEnding->bboxParams, circleEnding->sizeParams + 4, circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; default: break; } } if(parentEnding != ENDING_NONE && child){ if(linkKind){ [self linkParentPoint:&outPoint]; [self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint]; switch(parentEnding){ case ENDING_ARROW: PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4, parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_HOLLOW: PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4, parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_DOUBLE: PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4, parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_SOLID: PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4, parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_CIRCLE: PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4, parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; default: break; } } else { for(ptr = child;ptr;ptr = ptr->sibling){ [ptr linkPoint:&outPoint]; [self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint]; switch(parentEnding){ case ENDING_ARROW: PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4, parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_HOLLOW: PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4, parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_DOUBLE: PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4, parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_SOLID: PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4, parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; case ENDING_CIRCLE: PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4, parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle); break; default: break; } } } } return self; } - drawNodeOutline:(DrawState *)aState knobs:(BOOL)aBool { if(outline){ if(!NXEqualColor(aState->color,outlineColor)){ aState->color = outlineColor; NXSetColor(outlineColor); } if(aState->linewidth != linewidth){ aState->linewidth = linewidth; PSsetlinewidth(linewidth); } [gpath send:dps_ustroke cached:YES]; } if(selected && aBool){ if(delegate) PSselectfont(fontname,officialFontSize / [delegate docScale]); else PSselectfont(fontname,officialFontSize); aState->font = nil; if(!NXEqualColor(aState->color,NX_COLORBLACK)){ aState->color = NX_COLORBLACK; NXSetColor(NX_COLORBLACK); } controlX = gpath->bbox[0]; controlY = gpath->bbox[1]; controlPts[0] = gpath->bbox[2] - gpath->bbox[0]; controlPts[1] = 0; controlPts[2] = 0; controlPts[3] = gpath->bbox[3] - gpath->bbox[1]; controlPts[4] = -controlPts[0]; controlPts[5] = 0; PSWDrawControlPoints(controlX,controlY,controlPts,8,controlChars); if(!NXEqualColor(aState->color,NX_COLORDKGRAY)){ aState->color = NX_COLORDKGRAY; NXSetColor(NX_COLORDKGRAY); } PSWDrawControlPoints(controlX,controlY,controlPts,8,controlPrimChars); } return self; } - drawNodeCellsInView:aView attachments:(BOOL)aBool { if(hasVerknuepfung && aBool){ attachCellFrame.origin.x = pos.x + 23; attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1; [attachCell drawSelf:&attachCellFrame inView:aView]; } if(hasSound && aBool){ soundCellFrame.origin.x = pos.x + 37; soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1; [soundCell drawSelf:&soundCellFrame inView:aView]; } textCellFrame.origin.x = pos.x + deltaText.x; textCellFrame.origin.y = pos.y + deltaText.y; [textCell drawSelf:&textCellFrame inView:aView]; if(aBool && zipped){ zippedCellFrame.origin.x = pos.x + 7; zippedCellFrame.origin.y = pos.y - linewidth - zippedCellFrame.size.height - 1; [zippedCell drawSelf:&zippedCellFrame inView:aView]; } return self; } - setOffset:(NXCoord)aX :(NXCoord)aY { offset.x = aX; offset.y = aY; return self; } - (NXCoord)width { return width; } - (NXCoord)height { return height; } - (Polygon *)contour { return &contour; } - layout { Tree *c; if(!updateLayout) return nil; if(zipped){ [self layoutLeaf]; updateLayout = NO; return self; } for(c = child;c;c = [c sibling]) [c layout]; if(child) [self attachParent:[self join]]; else [self layoutLeaf]; updateLayout = NO; return self; } - getTextFrame:(NXRect *)aRect { NXSetRect(aRect, pos.x, pos.y + deltaText.y, width, textCellFrame.size.height); return self; } //************************************************************************ // tree data structure manipulation - sibling { return sibling; } - child { return child; } - parent { return parent; } - addTree:(Tree *)aTree { Tree *dummy; id undoManager; aTree->parent = self; aTree->linkKind = linkKind; aTree->biegFactor = biegFactor; aTree->parentDistance = parentDistance; aTree->shadow = shadow; aTree->shadowColor = shadowColor; aTree->border = border; if(!child){ child = aTree; } else { dummy = child; while(dummy->sibling) dummy = dummy->sibling; dummy->sibling = aTree; aTree->sibling = nil; } [self setUpdateLayout:YES]; if(delegate){ undoManager = [delegate undoManager]; [undoManager setUndoName:"Addition"]; [undoManager setRedoName:"Deletion"]; [[undoManager setUndoTarget:self] removeChild:aTree]; [delegate declareSelection:aTree]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)]; } return self; } - removeChild:(Tree *)aChild { Tree *dummy; id undoManager; int i; if(!child || !aChild) return self; [self setUpdateLayout:YES]; dummy = child; i = 0; if(dummy == aChild) child = aChild->sibling; else { while(dummy->sibling && dummy->sibling != aChild){ dummy = dummy->sibling; i++; } if(dummy->sibling == aChild) dummy->sibling = aChild->sibling; } aChild->parent = nil; aChild->sibling = nil; if(delegate){ [delegate declareSelection:nil]; undoManager = [delegate undoManager]; [undoManager freeUndoArgsOnRecordDiscard]; [undoManager setUndoName:"Deletion"]; [undoManager setRedoName:"Addition"]; [[undoManager setUndoTarget:self] insertTree:aChild at:i]; [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_ALL]; } else [aChild free]; return self; } - insertTree:(Tree *)aTree at:(int)aPos { Tree *dummy; id undoManager; int i; aTree->parent = self; aTree->linkKind = linkKind; aTree->biegFactor = biegFactor; aTree->parentDistance = parentDistance; aTree->shadow = shadow; aTree->shadowColor = shadowColor; aTree->border = border; if(!child){ child = aTree; } else { dummy = child; i = 0; while(dummy->sibling && i < aPos) dummy = dummy->sibling; aTree->sibling = dummy->sibling; dummy->sibling = aTree; } [self setUpdateLayout:YES]; if(delegate){ undoManager = [delegate undoManager]; [undoManager setUndoName:"Addition"]; [undoManager setRedoName:"Deletion"]; [[undoManager setUndoTarget:self] removeChild:aTree]; [delegate declareSelection:aTree]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)]; } return self; } //************************************************************************ // properties - attachFile:(const char *)aFileName { NXRect r; BOOL attachUpdate = YES; if(verknuepfung){ [verknuepfung free]; attachUpdate = NO; } verknuepfung = [[NXDataLink allocFromZone:[self zone]] initLinkedToFile:aFileName]; if(attachUpdate){ hasVerknuepfung = YES; [self getBounds:&r]; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; } return self; } - breakAttachment { NXRect r; if(!hasVerknuepfung) return self; [self getBounds:&r]; hasVerknuepfung = NO; [verknuepfung free]; verknuepfung = nil; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } - (BOOL)attached { return hasVerknuepfung; } - attachSound:(const char *)aSoundName { NXRect r; BOOL soundUpdate = YES; if(geraeusch){ [geraeusch free]; soundUpdate = NO; } geraeusch = [[Sound allocFromZone:[self zone]] initFromSoundfile:aSoundName]; if(soundUpdate){ hasSound = YES; [self getBounds:&r]; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; } return self; } - breakSound { NXRect r; if(!hasSound) return self; [self getBounds:&r]; hasSound = NO; [geraeusch free]; geraeusch = nil; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } - (BOOL)hasSound { return hasSound; } - attachedSound { return geraeusch; } - setSound:aSound { NXRect r; BOOL soundUpdate = YES; if(geraeusch){ [geraeusch free]; soundUpdate = NO; } geraeusch = aSound; if(soundUpdate){ hasSound = YES; [self getBounds:&r]; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; } return self; } - applyStyleToSubtree { Properties props; if(child){ [self getProps:&props]; [child promoteProps:&props]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; } return self; } - promoteProps:(Properties *)aProps { outline = aProps->outline; if([textCell font] != aProps->font){ [textCell setFont:aProps->font]; [self changeText]; [self setUpdateLayout:YES]; } if(pathKind != aProps->pathKind){ [gpath resetFill]; pathKind = aProps->pathKind; [self makePath]; [self setUpdateLayout:YES]; } fillColor = aProps->fillColor; outlineColor = aProps->outlineColor; [textCell setTextColor:aProps->textColor]; linewidth = aProps->linewidth; if(ending != aProps->ending){ ending = aProps->ending; } if(parentEnding != aProps->parentEnding){ parentEnding = aProps->parentEnding; } if(sibling) [sibling promoteProps:aProps]; if(child) [child promoteProps:aProps]; return self; } - getProps:(Properties *)aProps { aProps->outline = outline; aProps->font = [textCell font]; aProps->pathKind = pathKind; aProps->fillColor = fillColor; aProps->outlineColor = outlineColor; aProps->textColor = [textCell textColor]; aProps->linewidth = linewidth; aProps->ending = ending; aProps->parentEnding = parentEnding; return self; } - changeTo:(Properties *)aProps { NXRect r, dummyRect, pr; BOOL checkResize, makeUnion; [self getBounds:&r]; checkResize = NO; makeUnion = NO; outline = aProps->outline; if([textCell font] != aProps->font){ [textCell setFont:aProps->font]; [self changeText]; [self setUpdateLayout:YES]; checkResize = YES; } if(pathKind != aProps->pathKind){ [gpath resetFill]; pathKind = aProps->pathKind; [self makePath]; [self setUpdateLayout:YES]; checkResize = YES; } fillColor = aProps->fillColor; outlineColor = aProps->outlineColor; [textCell setTextColor:aProps->textColor]; if(ending != aProps->ending){ ending = aProps->ending; makeUnion = YES; } if(parentEnding != aProps->parentEnding){ parentEnding = aProps->parentEnding; if(parent){ [parent getBounds:&pr]; [delegate updateViewsDirty:YES rect:&pr flag:UPDATE_TREEVIEW]; } } if(aProps->linewidth != linewidth){ linewidth = aProps->linewidth; makeUnion = YES; } if(makeUnion){ [self getBounds:&dummyRect]; NXUnionRect(&dummyRect, &r); } if(checkResize) [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; else [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } //************************************************************************ // tree properties - (NXCoord)border { return border; } - setBorder:(NXCoord)aBorder { border = aBorder; [self setUpdateLayout:YES]; if(child) [child setBorder:aBorder]; if(sibling) [sibling setBorder:aBorder]; if(!parent) [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - (NXCoord)parentDistance { return parentDistance; } - setParentDistance:(NXCoord)aParentDistance { parentDistance = aParentDistance; [self setUpdateLayout:YES]; if(child) [child setParentDistance:aParentDistance]; if(sibling) [sibling setParentDistance:aParentDistance]; if(!parent) [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - (BOOL)shadow { return shadow; } - setShadow:(BOOL)aShadow { shadow = aShadow; if(child) [child setShadow:aShadow]; if(sibling) [sibling setShadow:aShadow]; if(!parent) [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - (NXColor)shadowColor { return shadowColor; } - setShadowColor:(NXColor)aColor { shadowColor = aColor; if(child) [child setShadowColor:aColor]; if(sibling) [sibling setShadowColor:aColor]; if(!parent) [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW]; return self; } - (int)linkKind { return linkKind; } - setLinkKind:(int)aLinkKind { linkKind = aLinkKind; if(child) [child setLinkKind:aLinkKind]; if(sibling) [sibling setLinkKind:aLinkKind]; if(!parent) [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW]; return self; } - changeLinkKind { linkKind = (linkKind + 1) % 2; if(child) [child changeLinkKind]; if(sibling) [sibling changeLinkKind]; if(!parent) [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW]; return self; } - (float)biegFactor { return biegFactor; } - setBiegFactor:(float)aBiegFactor { biegFactor = aBiegFactor; if(child) [child setBiegFactor:aBiegFactor]; if(sibling) [sibling setBiegFactor:aBiegFactor]; if(!parent) [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW]; return self; } //************************************************************************ // node properties - (const char *)label { return [textCell stringValue]; } - setLabel:(const char *)aLabel { [textCell setStringValue:aLabel]; [self changeText]; [self setUpdateLayout:YES]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - setRTFDescription:(const char *)aDescription { [nodeDescription setRTF:aDescription]; [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW]; return self; } - setDescription:(const char *)aDescription { [nodeDescription setAscii:aDescription]; [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW]; return self; } - changeDescriptionFrom:aTextView { [nodeDescription setRTFFromTextView:aTextView]; [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_NONE]; return self; } - putDescriptionIn:aTextView { [aTextView replaceSelWithRichText:[nodeDescription stream]]; [nodeDescription freeStream]; return self; } - (int)pathKind { return pathKind; } - setPathKind:(int)aPathKind { [gpath resetFill]; pathKind = aPathKind; [self makePath]; [self setUpdateLayout:YES]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - (BOOL)outline { return outline; } - setOutline:(BOOL)aOutline { NXRect r; outline = aOutline; [self getBounds:&r]; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } - font { return [textCell font]; } - changeFont:sender { [textCell setFont:[sender convertFont:[textCell font]]]; [self changeText]; [self setUpdateLayout:YES]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - (NXColor)fillColor { return fillColor; } - setFillColor:(NXColor)aColor { NXRect r; fillColor = aColor; [self getBounds:&r]; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } - (NXColor)outlineColor { return outlineColor; } - setOutlineColor:(NXColor)aColor { NXRect r; outlineColor = aColor; [self getBounds:&r]; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } - (NXColor)textColor { return [textCell textColor]; } - setTextColor:(NXColor)aColor { NXRect r; [textCell setTextColor:aColor]; [self getBounds:&r]; [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } - (float)linewidth { return linewidth; } - setLinewidth:(float)aLinewidth { NXRect r; if(aLinewidth > linewidth){ linewidth = aLinewidth; [self getBounds:&r]; } else { [self getBounds:&r]; linewidth = aLinewidth; } [delegate updateViewsDirty:YES rect:&r flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - (BOOL)zipped { return zipped; } - changeZipped { zipped = ! zipped; [self setUpdateLayout:YES]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } - (int)ending { return ending; } - setEnding:(int)aEnding { NXRect r,br; [self getBounds:&br]; ending = aEnding; [self getBounds:&r]; NXUnionRect(&br,&r); [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } - (int)parentEnding; { return parentEnding; } - setParentEnding:(int)aEnding { NXRect r,br; [self getBounds:&br]; parentEnding = aEnding; [self getBounds:&r]; NXUnionRect(&br,&r); [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW]; return self; } //************************************************************************ // misc - (BOOL)selected { return selected; } - setSelected:(BOOL)aBOOL { selected = aBOOL; return self; } - setUpdateLayout:(BOOL)aBool { Tree *dummy; updateLayout = aBool; if(aBool) if(parent){ [parent setUpdateLayout:YES]; dummy = [parent child]; dummy->updateLayout = YES; while([dummy sibling]){ dummy = [dummy sibling]; dummy->updateLayout = YES; } } return self; } - positionFrom:(const NXPoint *)aPoint { pos.x = aPoint->x + offset.x; pos.y = aPoint->y + offset.y; [self changePos]; if(child) [child positionFrom:&pos]; if(sibling) [sibling positionFrom:&pos]; return self; } - setPositionTo:(const NXPoint *)aPoint { pos.x = aPoint->x; pos.y = aPoint->y; [self changePos]; if(child) [child positionFrom:&pos]; if(sibling) [sibling positionFrom:&pos]; return self; } - linkPoint:(NXPoint *)aPoint { aPoint->x = pos.x + deltaLink.x; aPoint->y = pos.y + deltaLink.y; return self; } - linkParentPoint:(NXPoint *)aPoint { aPoint->x = pos.x + width + (parentDistance + border) * (1 - biegFactor) / 2; aPoint->y = pos.y + deltaLink.y; return self; } - linkChildPoint:(NXPoint *)aPoint { aPoint->x = pos.x - (parentDistance + border) * (1 - biegFactor) / 2; aPoint->y = pos.y + deltaLink.y; return self; } - delegate { return delegate; } - setDelegate:aDelegate { id oldDelegate; oldDelegate = delegate; delegate = aDelegate; return oldDelegate; } - setDelegateRecursive:aDelegate { delegate = aDelegate; if(child) [child setDelegateRecursive:aDelegate]; if(sibling) [sibling setDelegateRecursive:aDelegate]; return self; } - textDidEnd:textObject endChar:(unsigned short)endChar { int maxlen,length; char *buffer; NXStream *stream; [textObject removeFromSuperview]; [textObject setDelegate:nil]; [textObject setSel:0 :0]; if([textObject textLength]){ stream = NXOpenMemory(NULL, 0, NX_WRITEONLY); [textObject writeText:stream]; NXGetMemoryBuffer(stream, &buffer, &length, &maxlen); [textCell setStringValue:buffer]; NXCloseMemory(stream, NX_FREEBUFFER); } [self changeText]; [self setUpdateLayout:YES]; [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)]; return self; } //************************************************************************* // streaming to diagram2 format - (unsigned)dgSymbolNr { return dgSymbolNr; } - dgWriteSymbol:(NXStream *)stream buffer:(char *)buffer filename:(const char *)aName { id font; char rtfItalic[3], rtfBold[3]; unsigned i; int err; dgSymbolNr = [Tree dgNextNr]; sprintf(buffer,"symbol %d\n", dgSymbolNr); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\tshape \"%s\"\n", dgShapeSymbolName[pathKind]); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\tlocation %.2f %.2f\n",pos.x,pos.y); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\tsize %.2f %.2f\n",width,height); NXWrite(stream,buffer,strlen(buffer)); if(outline) NXWrite(stream,"\tframed\n", 8); if(fill) NXWrite(stream,"\tfilled\n", 8); if(shadow) NXWrite(stream,"\tshadowed\n", 10); if(outline){ i = 0; while(i < [dgColorList colorCount] && !NXEqualColor(outlineColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]])) i++; if(i == [dgColorList colorCount]){ sprintf(buffer,"woodColorIndexed%d",i); [dgColorList setColorNamed:buffer color:outlineColor]; } sprintf(buffer,"\tframeColor colorIndex %d\n",i); NXWrite(stream,buffer,strlen(buffer)); } if(fill){ i = 0; while(i < [dgColorList colorCount] && !NXEqualColor(fillColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]])) i++; if(i == [dgColorList colorCount]){ sprintf(buffer,"woodColorIndexed%d",i); [dgColorList setColorNamed:buffer color:fillColor]; } sprintf(buffer,"\tfillColor colorIndex %d\n",i); NXWrite(stream,buffer,strlen(buffer)); } if(shadow){ i = 0; while(i < [dgColorList colorCount] && !NXEqualColor(shadowColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]])) i++; if(i == [dgColorList colorCount]){ sprintf(buffer,"woodColorIndexed%d",i); [dgColorList setColorNamed:buffer color:shadowColor]; } sprintf(buffer,"\tshadowColor colorIndex %d\n",i); NXWrite(stream,buffer,strlen(buffer)); } sprintf(buffer,"\tlineWidth %.2f\n",linewidth); NXWrite(stream,buffer,strlen(buffer)); if(hasVerknuepfung){ sprintf(buffer,"\tattachment \"%s\"\n",[verknuepfung sourceFilename]); NXWrite(stream,buffer,strlen(buffer)); } if(hasSound){ sprintf(buffer,"%s/Sound%d.snd",aName,dgSymbolNr); err = [geraeusch writeSoundfile:buffer]; if(err) NXRunAlertPanel("Save Sound","Cannot write %s","OK",NULL,NULL,buffer); else { sprintf(buffer,"\tsoundFile \"Sound%d.snd\"\n",dgSymbolNr); NXWrite(stream,buffer,strlen(buffer)); } } font = [textCell font]; if(strstr([font displayName], "Bold")) strcpy(rtfBold,"b"); else strcpy(rtfBold,"b0"); if(strstr([font displayName], "Italic") || strstr([font displayName], "Oblique")) strcpy(rtfItalic,"i"); else strcpy(rtfItalic,"i0"); sprintf(buffer, dgRtfControlName, [font familyName], rtfBold, rtfItalic, [font pointSize] * 2, [textCell stringValue]); NXWrite(stream,buffer,strlen(buffer)); NXWrite(stream,"\n",1); NXWrite(stream,"\ttextPlacement middle\n", 22); NXWrite(stream,dgEndName, strlen(dgEndName)); if(child) [child dgWriteSymbol:stream buffer:buffer filename:aName]; if(sibling) [sibling dgWriteSymbol:stream buffer:buffer filename:aName]; return self; } - dgWriteVertex:(NXStream *)stream buffer:(char *)buffer { NXPoint p2,p3; if(parent){ dgVertexNr[0] = [Tree dgNextNr]; sprintf(buffer,"vertex %d\n",dgVertexNr[0]); NXWrite(stream,buffer,strlen(buffer)); NXWrite(stream,dgEndName, strlen(dgEndName)); dgVertexNr[1] = [Tree dgNextNr]; sprintf(buffer,"vertex %d\n",dgVertexNr[1]); NXWrite(stream,buffer,strlen(buffer)); NXWrite(stream,dgEndName, strlen(dgEndName)); if(linkKind){ [parent linkParentPoint:&p3]; [self linkChildPoint:&p2]; dgVertexNr[2] = [Tree dgNextNr]; sprintf(buffer,"vertex %d\n",dgVertexNr[2]); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\tlocation %.2f %.2f\n", p2.x, p2.y); NXWrite(stream,buffer,strlen(buffer)); NXWrite(stream,dgEndName, strlen(dgEndName)); dgVertexNr[3] = [Tree dgNextNr]; sprintf(buffer,"vertex %d\n",dgVertexNr[3]); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\tlocation %.2f %.2f\n", p3.x, p3.y); NXWrite(stream,buffer,strlen(buffer)); NXWrite(stream,dgEndName, strlen(dgEndName)); } } if(child) [child dgWriteVertex:stream buffer:buffer]; if(sibling) [sibling dgWriteVertex:stream buffer:buffer]; return self; } - dgWriteLine:(NXStream *)stream buffer:(char *)buffer { if(parent){ sprintf(buffer,"line %d\n",[Tree dgNextNr]); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\tfrom %d\n",dgSymbolNr); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\tto %d\n",[parent dgSymbolNr]); NXWrite(stream,buffer,strlen(buffer)); if(ending != ENDING_NONE){ switch(ending){ case ENDING_ARROW: sprintf(buffer,"\ttailType arrow\n"); break; case ENDING_HOLLOW: sprintf(buffer,"\ttailType hollowArrow\n"); break; case ENDING_DOUBLE: sprintf(buffer,"\ttailType doubleArrow\n"); break; case ENDING_SOLID: sprintf(buffer,"\ttailType solidCircle\n"); break; case ENDING_CIRCLE: sprintf(buffer,"\ttailType circle\n"); break; default: break; } NXWrite(stream,buffer,strlen(buffer)); } if([parent parentEnding] != ENDING_NONE){ switch([parent parentEnding]){ case ENDING_ARROW: sprintf(buffer,"\theadType arrow\n"); break; case ENDING_HOLLOW: sprintf(buffer,"\theadType hollowArrow\n"); break; case ENDING_DOUBLE: sprintf(buffer,"\theadType doubleArrow\n"); break; case ENDING_SOLID: sprintf(buffer,"\theadType solidCircle\n"); break; case ENDING_CIRCLE: sprintf(buffer,"\theadType circle\n"); break; default: break; } NXWrite(stream,buffer,strlen(buffer)); } sprintf(buffer,"\t%d\n",dgVertexNr[0]); NXWrite(stream,buffer,strlen(buffer)); if(linkKind){ sprintf(buffer,"\t%d\n",dgVertexNr[2]); NXWrite(stream,buffer,strlen(buffer)); sprintf(buffer,"\t%d\n",dgVertexNr[3]); NXWrite(stream,buffer,strlen(buffer)); } sprintf(buffer,"\t%d\n",dgVertexNr[1]); NXWrite(stream,buffer,strlen(buffer)); NXWrite(stream,dgEndName, strlen(dgEndName)); } if(child) [child dgWriteLine:stream buffer:buffer]; if(sibling) [sibling dgWriteLine:stream buffer:buffer]; return self; } + saveToD2Tree:aTree fromView:aView toFile:(const char *)aFile printInfo:aPrintInfo { NXStream *stream; NXTypedStream *typedStream; char fileNameBuffer[MAXPATHLEN],commandBuffer[MAXPATHLEN + 20]; char buffer[1024]; NXRect dummyRect; dgColorList = [[NXColorList alloc] init]; [aView getBounds:&dummyRect]; dgNextNr = 0; stream = NXOpenMemory(NULL,0,NX_WRITEONLY); NXWrite(stream, dgDocuBegin,strlen(dgDocuBegin)); strcpy(fileNameBuffer,aFile); sprintf(commandBuffer,"mkdir %s",fileNameBuffer); system(commandBuffer); if(aTree){ NXWrite(stream, dgSymbolComment,strlen(dgSymbolComment)); [aTree dgWriteSymbol:stream buffer:buffer filename:aFile]; NXWrite(stream, dgVertexComment,strlen(dgVertexComment)); [aTree dgWriteVertex:stream buffer:buffer]; NXWrite(stream, dgLineComment,strlen(dgLineComment)); [aTree dgWriteLine:stream buffer:buffer]; } strcpy(fileNameBuffer,aFile); strcat(fileNameBuffer,"/DiagramText"); NXSaveToFile(stream, fileNameBuffer); NXCloseMemory(stream,NX_FREEBUFFER); strcpy(fileNameBuffer,aFile); strcat(fileNameBuffer,"/PrintInfo"); stream = NXOpenMemory(NULL,0,NX_WRITEONLY); typedStream = NXOpenTypedStream(stream,NX_WRITEONLY); NXWriteRootObject(typedStream,aPrintInfo); NXCloseTypedStream(typedStream); NXSeek(stream, - 1, NX_FROMCURRENT); NXWrite(stream,dgPrintInfoEnd,6); NXSaveToFile(stream, fileNameBuffer); NXCloseMemory(stream,NX_FREEBUFFER); strcpy(fileNameBuffer,aFile); strcat(fileNameBuffer,"/Colors.clr"); [dgColorList saveTo:fileNameBuffer]; [dgColorList free]; dgColorList = nil; return self; } + (unsigned)dgNextNr { dgNextNr++; return dgNextNr; } //************************************************************************* // streaming - writeStyleToPBStream:(NXTypedStream *)stream { NXWriteTypes(stream,"iiiif",&outline,&pathKind,&ending,&parentEnding,&linewidth); NXWriteColor(stream,fillColor); NXWriteColor(stream,outlineColor); NXWriteColor(stream,[textCell textColor]); NXWriteObject(stream,[textCell font]); return self; } - readStyleFromPBStream:(NXTypedStream *)stream { Properties props; NXReadTypes(stream,"iiiif",&(props.outline),&(props.pathKind),&(props.ending),&(props.parentEnding),&(props.linewidth)); props.fillColor = NXReadColor(stream); props.outlineColor = NXReadColor(stream); props.textColor = NXReadColor(stream); props.font = NXReadObject(stream); [self changeTo:&props]; return self; } - writeToPBStream:(NXTypedStream *)stream { Tree *tmpParent, *tmpSibling; tmpParent = parent; tmpSibling = sibling; parent = sibling = nil; NXWriteRootObject(stream, self); parent = tmpParent; sibling = tmpSibling; return self; } - writeToMMAPB:(NXStream *)stream { if(child){ NXWrite(stream,"{",1); NXWrite(stream,[textCell stringValue],strlen([textCell stringValue])); NXWrite(stream,", ",2); [child writeToMMAPB:stream]; NXWrite(stream,"}",1); } else { NXWrite(stream,[textCell stringValue],strlen([textCell stringValue])); } if(sibling){ NXWrite(stream,", ",2); [sibling writeToMMAPB:stream]; } return self; } - write:(NXTypedStream *)stream { //BOOL hasChild,hasSibling; int hasChild,hasSibling; id retval; NX_DURING [super write:stream]; hasChild = (child ? YES : NO); hasSibling = (sibling ? YES :NO); NXWriteTypes(stream,"ii",&hasVerknuepfung,&hasSound); NXWriteTypes(stream,"iiff",&hasChild,&hasSibling,&parentDistance,&border); NXWriteTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor); NXWriteTypes(stream,"ii",&ending,&parentEnding); NXWriteColor(stream,fillColor); NXWriteColor(stream,outlineColor); NXWriteColor(stream,shadowColor); NXWriteObject(stream,textCell); NXWriteObject(stream,nodeDescription); NXWriteObjectReference(stream, parent); if(hasChild) NXWriteObject(stream, child); if(hasSibling) NXWriteObject(stream, sibling); if(hasVerknuepfung) NXWriteObject(stream,verknuepfung); if(hasSound) NXWriteObject(stream,geraeusch); retval = self; NX_HANDLER retval = nil; NX_ENDHANDLER return retval; } - read:(NXTypedStream *)stream { //BOOL hasChild,hasSibling; int hasChild,hasSibling; id retval; NX_DURING NXSetTypedStreamZone(stream, [self zone]); [super read:stream]; NXReadTypes(stream,"ii",&hasVerknuepfung,&hasSound); NXReadTypes(stream,"iiff", &hasChild,&hasSibling,&parentDistance,&border); if(NXTypedStreamClassVersion(stream, "Tree") == [Tree version]) NXReadTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor); else { NXReadTypes(stream,"iiiiif",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth); biegFactor = 0.33; } NXReadTypes(stream,"ii",&ending,&parentEnding); fillColor = NXReadColor(stream); outlineColor = NXReadColor(stream); shadowColor = NXReadColor(stream); textCell = NXReadObject(stream); nodeDescription = NXReadObject(stream); parent = NXReadObject(stream); if(hasChild) child = NXReadObject(stream); else child = nil; if(hasSibling) sibling = NXReadObject(stream); else sibling = nil; if(hasVerknuepfung) verknuepfung = NXReadObject(stream); if(hasSound) geraeusch = NXReadObject(stream); retval = self; NX_HANDLER retval = nil; NX_ENDHANDLER return retval; } - awake { [super awake]; contour.lower.head = contour.upper.head = NULL; contour.lower.tail = contour.upper.tail = NULL; offset.x = offset.y = 0.0; pos.x = pos.y = 0.0; gpath = [[UPath allocFromZone:[self zone]] init]; deltaShadow.x = deltaShadow.y = 3; placer.rect[0] = placer.rect[1] = 6; placer.roundRect[0] = placer.roundRect[1] = 6; placer.roundRect[2] = 3; placer.ellipse[0] = 6; placer.ellipse[1] = 4; placer.rhomb[0] = 12; placer.rhomb[1] = 3; placer.hexagon[0] = 6; placer.hexagon[1] = 6; placer.hexagon[2] = 1; [self makePath]; selected = NO; updateLayout = YES; delegate = nil; fill = YES; return self; } //************************************************************************* // SearchableText support - searchTreeFor:(const char *)pattern untilNode:endNode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(int *)positionS size:(int *)sizeS { MiscString *labelS; id result; int resultPos; result = nil; resultPos = -1; labelS = [[MiscString alloc] initString:[self label]]; if(regexpr) resultPos = [labelS spotOfRegex:pattern caseSensitive:cases length:sizeS]; else resultPos = [labelS spotOfStr:pattern caseSensitive:cases]; if(resultPos > -1){ *positionS = resultPos; if(!regexpr) *sizeS = strlen(pattern); result = self; } else if(self == endNode) return nil; else { if(rev) result = [[self previousInDepth] searchTreeFor:pattern untilNode:endNode reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS]; else result = [[self nextInDepth] searchTreeFor:pattern untilNode:endNode reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS]; } [labelS free]; return result; } - replaceTreeFor:(const char *)pattern with:(const char *)replacement untilNode:endNode regexpr:(BOOL)regexpr cases:(BOOL)cases result:(int *)result { MiscString *labelS; int nr; nr = 0; labelS = [[MiscString alloc] initString:[self label]]; if(regexpr) nr = [labelS replaceEveryOccurrenceOfRegex:pattern with:replacement caseSensitive:cases]; else nr = [labelS replaceEveryOccurrenceOf:pattern with:replacement caseSensitive:cases]; if(nr > 0){ [textCell setStringValue:[labelS stringValue]]; [self changeText]; [self setUpdateLayout:YES]; } *result += nr; if(self != endNode) [[self nextInDepth] replaceTreeFor:pattern with:replacement untilNode:endNode regexpr:regexpr cases:cases result:result]; [labelS free]; return self; } - nextInDepth { Tree *r, *t; if(child) r = child; else if(sibling) r = sibling; else { t = self; while([t parent] && ![t sibling]) t = [t parent]; if([t sibling]) r = [t sibling]; else r = t; } return r; } - previousInDepth { Tree *r, *t; if(!parent) return [self lastInDepth]; if([parent child] == self) return parent; else { r = [parent child]; t = [r sibling]; while(t != self){ r = t; t = [t sibling]; } return [r lastInDepth]; } } - lastInDepth { Tree *r, *t; r = self; t = child; while(t){ r = t; while(t){ r = t; t = [t sibling]; } t = [r child]; } return r; } - (BOOL)replaceLabelSelectionWith:(const char *)replacement pos:(int)aPos size:(int)aSize { MiscString *labelS, *pattern; int nr; BOOL result = NO; labelS = [[MiscString alloc] initString:[self label]]; if(aPos + aSize > [labelS length]) return result; pattern = [labelS midFrom:aPos to:(aPos + aSize - 1) fromZone:[self zone]]; nr = [labelS replaceEveryOccurrenceOfString:pattern with:replacement caseSensitive:YES]; if(nr > 0){ [textCell setStringValue:[labelS stringValue]]; [self changeText]; [self setUpdateLayout:YES]; result = YES; } [labelS free]; return result; } @end @implementation Tree(PrivateMethods) - attachParent:(NXCoord)h { NXCoord x,y1,y2; x = border + parentDistance; y2 = (h - height) / 2 - border; y1 = y2 + height + 2 * border - h; [child setOffset:x + width :y1]; contour.upper.head = allocLine(width,0,allocLine(x,y1,contour.upper.head,[self zone]),[self zone]); contour.lower.head = allocLine(width,0,allocLine(x,y2,contour.lower.head,[self zone]),[self zone]); return self; } - layoutLeaf { if(contour.lower.head) freePolyline(contour.lower.head); if(contour.upper.head) freePolyline(contour.upper.head); contour.upper.tail = allocLine(width + 2 * border,0,NULL,[self zone]); contour.upper.head = contour.upper.tail; contour.lower.tail = allocLine(0, - height - 2 * border,NULL,[self zone]); contour.lower.head = allocLine(width + 2 * border,0,contour.lower.tail,[self zone]); return self; } - (NXCoord)join { Tree *c; NXCoord d,h,sum; Polygon dummy; c = child; if(contour.lower.head) freePolyline(contour.lower.head); if(contour.upper.head) freePolyline(contour.upper.head); if(c->contour.upper.head) contour.upper.head = copyPolyline(c->contour.upper.head,&(contour.upper.tail),[self zone]); else contour.upper.head = contour.upper.tail = NULL; if(c->contour.lower.head) contour.lower.head = copyPolyline(c->contour.lower.head,&(contour.lower.tail),[self zone]); else contour.lower.head = contour.lower.tail = NULL; sum = h = [c height] + 2 * [c border]; c = [c sibling]; while(c) { if(c->contour.upper.head) dummy.upper.head = copyPolyline(c->contour.upper.head,&(dummy.upper.tail),[self zone]); else dummy.upper.head = dummy.upper.tail = NULL; if(c->contour.lower.head) dummy.lower.head = copyPolyline(c->contour.lower.head,&(dummy.lower.tail),[self zone]); else dummy.lower.head = dummy.lower.tail = NULL; d = merge(&contour,&dummy,[self zone]); [c setOffset:0 :d + h]; h = [c height] + 2 * [c border]; sum += d + h; c = [c sibling]; } return sum; } - makePath { float r; [self calcWidthHeight]; switch(pathKind){ case RECT_NODETYPE: [gpath moveto:pos.x :pos.y]; [gpath rlineto:width :0.0]; [gpath rlineto:0.0 :height]; [gpath rlineto:-width :0.0]; [gpath rlineto:0.0 :-height]; break; case ROUNDRECT_NODETYPE: r = MIN(width / 3, height / 3); [gpath moveto:pos.x :pos.y + r]; [gpath lineto:pos.x :pos.y + height - r]; [gpath curveto:pos.x :pos.y + height - r + r * 0.5519 :pos.x + r - r * 0.5519 :pos.y + height :pos.x + r :pos.y + height]; [gpath lineto:pos.x + width - r :pos.y + height]; [gpath curveto:pos.x + width - r + r * 0.5519 :pos.y + height :pos.x + width :pos.y + height - r + r * 0.5519 :pos.x + width :pos.y + height - r]; [gpath lineto:pos.x + width :pos.y + r]; [gpath curveto:pos.x + width :pos.y + r - r * 0.5519 :pos.x + width - r + r * 0.5519 :pos.y :pos.x + width - r :pos.y]; [gpath lineto:pos.x + r :pos.y]; [gpath curveto:pos.x + r - r * 0.5519 :pos.y :pos.x :pos.y + r - r * 0.5519 :pos.x :pos.y + r]; /* [gpath moveto:(pos.x + height / 3) :pos.y]; [gpath arct:(pos.x + width) :pos.y :(pos.x + width) :(pos.y + height) :height / 3]; [gpath arct:(pos.x + width) :(pos.y + height) :pos.x :(pos.y + height) :height / 3]; [gpath arct:pos.x :(pos.y + height) :pos.x :pos.y : height / 3]; [gpath arct:pos.x :pos.y :(pos.x + width) :pos.y :height / 3]; */ break; case ELLIPSE_NODETYPE: [gpath moveto:(pos.x + width) :(pos.y + height / 2)]; [gpath curveto:(pos.x + width) :(pos.y + height / 2 * 0.445) :(pos.x + width / 2 * 1.555) :pos.y :(pos.x + width / 2) :pos.y]; [gpath curveto:(pos.x + width / 2 * 0.445) :pos.y :pos.x :(pos.y + height / 2 * 0.445) :pos.x :(pos.y + height / 2)]; [gpath curveto:pos.x :(pos.y + height /2 * 1.555) :(pos.x + width / 2 * 0.445) :(pos.y + height) :(pos.x + width / 2) :(pos.y + height)]; [gpath curveto:(pos.x + width / 2 * 1.555) :(pos.y + height) :(pos.x + width) :(pos.y + height / 2 * 1.555) :(pos.x + width) :(pos.y + height / 2)]; break; case RHOMB_NODETYPE: [gpath moveto:pos.x + width / 2 :pos.y]; [gpath rlineto:width / 2 :height / 2]; [gpath rlineto:-width / 2 :height / 2]; [gpath rlineto:-width / 2 :-height / 2]; [gpath rlineto:width / 2 :-height / 2]; break; case HEXAGON_NODETYPE: [gpath moveto:pos.x + placer.hexagon[0] :pos.y]; [gpath rlineto:(width - 2 * placer.hexagon[0]) :0]; [gpath rlineto:placer.hexagon[0] :height /2]; [gpath rlineto:-placer.hexagon[0] :height /2]; [gpath rlineto:(- width + 2 * placer.hexagon[0]) :0]; [gpath rlineto:-placer.hexagon[0] :-height / 2]; [gpath rlineto:placer.hexagon[0] :-height / 2]; break; default: break; } return self; } - changeText { [self calcWidthHeight]; [self fillGPathParams]; gpath->bbox[2] = gpath->bbox[0] + width; gpath->bbox[3] = gpath->bbox[1] + height; return self; } - changePos { NXPoint delta; [self fillGPathParams]; switch(pathKind){ case RECT_NODETYPE: delta.x = gpath->params[0] - gpath->bbox[0]; delta.y = gpath->params[1] - gpath->bbox[1]; break; case ROUNDRECT_NODETYPE: delta.x = gpath->params[0] - gpath->bbox[0]; delta.y = gpath->params[23] - gpath->bbox[1]; break; case ELLIPSE_NODETYPE: delta.x = gpath->params[10] - gpath->bbox[0]; delta.y = gpath->params[5] - gpath->bbox[1]; break; case RHOMB_NODETYPE: delta.x = gpath->params[0] - gpath->bbox[0] - width / 2; delta.y = gpath->params[1] - gpath->bbox[1]; break; case HEXAGON_NODETYPE: delta.x = gpath->params[0] - gpath->bbox[0] - placer.hexagon[0]; delta.y = gpath->params[1] - gpath->bbox[1]; break; default: break; } gpath->bbox[0] += delta.x; gpath->bbox[1] += delta.y; gpath->bbox[2] += delta.x; gpath->bbox[3] += delta.y; return self; } - fillGPathParams { float r; switch(pathKind){ case RECT_NODETYPE: gpath->params[0] = pos.x; gpath->params[1] = pos.y; gpath->params[2] = width; gpath->params[3] = 0.0; gpath->params[4] = 0.0; gpath->params[5] = height; gpath->params[6] = -width; gpath->params[7] = 0.0; gpath->params[8] = 0.0; gpath->params[9] = -height; break; case ROUNDRECT_NODETYPE: r = MIN(width / 3, height / 3); gpath->params[0] = pos.x; gpath->params[1] = pos.y + r; gpath->params[2] = pos.x; gpath->params[3] = pos.y + height - r; gpath->params[4] = pos.x; gpath->params[5] = pos.y + height - r + r * 0.5519; gpath->params[6] = pos.x + r - r * 0.5519; gpath->params[7] = pos.y + height; gpath->params[8] = pos.x + r; gpath->params[9] = pos.y + height; gpath->params[10] = pos.x + width - r; gpath->params[11] = pos.y + height; gpath->params[12] = pos.x + width - r + r * 0.5519; gpath->params[13] = pos.y + height; gpath->params[14] = pos.x + width; gpath->params[15] = pos.y + height - r + r * 0.5519; gpath->params[16] = pos.x + width; gpath->params[17] = pos.y + height - r; gpath->params[18] = pos.x + width; gpath->params[19] = pos.y + r; gpath->params[20] = pos.x + width; gpath->params[21] = pos.y + r - r * 0.5519; gpath->params[22] = pos.x + width - r + r * 0.5519; gpath->params[23] = pos.y; gpath->params[24] = pos.x + width - r; gpath->params[25] = pos.y; gpath->params[26] = pos.x + r; gpath->params[27] = pos.y; gpath->params[28] = pos.x + r - r * 0.5519; gpath->params[29] = pos.y; gpath->params[30] = pos.x; gpath->params[31] = pos.y + r - r * 0.5519; gpath->params[32] = pos.x; gpath->params[33] = pos.y + r; /* gpath->params[0] = pos.x + height / 3; gpath->params[1] = pos.y; gpath->params[2] = pos.x + width; gpath->params[3] = pos.y; gpath->params[4] = pos.x + width; gpath->params[5] = pos.y + height; gpath->params[6] = height / 3; gpath->params[7] = pos.x + width; gpath->params[8] = pos.y + height; gpath->params[9] = pos.x; gpath->params[10] = pos.y + height; gpath->params[11] = height / 3; gpath->params[12] = pos.x; gpath->params[13] = pos.y + height; gpath->params[14] = pos.x; gpath->params[15] = pos.y; gpath->params[16] = height / 3; gpath->params[17] = pos.x; gpath->params[18] = pos.y; gpath->params[19] = pos.x + width; gpath->params[20] = pos.y; gpath->params[21] = height / 3; */ break; case ELLIPSE_NODETYPE: gpath->params[0] = pos.x + width; gpath->params[1] = pos.y + height / 2; gpath->params[2] = pos.x + width; gpath->params[3] = pos.y + height / 2 * 0.445; gpath->params[4] = pos.x + width / 2 * 1.555; gpath->params[5] = pos.y; gpath->params[6] = pos.x + width / 2; gpath->params[7] = pos.y; gpath->params[8] = pos.x + width / 2 * 0.445; gpath->params[9] = pos.y; gpath->params[10] = pos.x; gpath->params[11] = pos.y + height / 2 * 0.445; gpath->params[12] = pos.x; gpath->params[13] = pos.y + height / 2; gpath->params[14] = pos.x; gpath->params[15] = pos.y + height /2 * 1.555; gpath->params[16] = pos.x + width / 2 * 0.445; gpath->params[17] = pos.y + height; gpath->params[18] = pos.x + width / 2; gpath->params[19] = pos.y + height; gpath->params[20] = pos.x + width / 2 * 1.555; gpath->params[21] = pos.y + height; gpath->params[22] = pos.x + width; gpath->params[23] = pos.y + height / 2 * 1.555; gpath->params[24] = pos.x + width; gpath->params[25] = pos.y + height / 2; break; case RHOMB_NODETYPE: gpath->params[0] = pos.x + width / 2; gpath->params[1] = pos.y; gpath->params[2] = width / 2; gpath->params[3] = height / 2; gpath->params[4] = -width / 2; gpath->params[5] = height / 2; gpath->params[6] = -width / 2; gpath->params[7] = -height / 2; gpath->params[8] =width / 2; gpath->params[9] = -height / 2; break; case HEXAGON_NODETYPE: gpath->params[0] = pos.x + placer.hexagon[0]; gpath->params[1] = pos.y; gpath->params[2] = width - 2 * placer.hexagon[0]; gpath->params[3] = 0; gpath->params[4] = placer.hexagon[0]; gpath->params[5] = height / 2; gpath->params[6] = -placer.hexagon[0]; gpath->params[7] = height / 2; gpath->params[8] =-width + 2 * placer.hexagon[0]; gpath->params[9] = 0; gpath->params[10] =-placer.hexagon[0]; gpath->params[11] = -height / 2; gpath->params[12] = placer.hexagon[0]; gpath->params[13] = -height / 2; break; default: break; } return self; } - calcWidthHeight { NXCoord v1,v2; NXSize textSize; [textCell calcCellSize:&textSize]; switch(pathKind){ case RECT_NODETYPE: width = textSize.width + 2 * placer.rect[0]; height = textSize.height + 2 * placer.rect[1]; break; case ROUNDRECT_NODETYPE: width = textSize.width + 2 * placer.roundRect[0]; height = textSize.height + 2 * placer.roundRect[1]; break; case ELLIPSE_NODETYPE: height = textSize.height + 2 * placer.ellipse[0]; v1 = placer.ellipse[0] - placer.ellipse[1]; v2 = placer.ellipse[0] + height + placer.ellipse[1]; width = (textSize.width / 2 + placer.ellipse[1]) * height / sqrt(v1 * v2); break; case RHOMB_NODETYPE: height = textSize.height + 2 * placer.rhomb[0]; v1 = placer.rhomb[0] - placer.rhomb[1]; width = (textSize.width / 2 + placer.rhomb[1]) * height / v1; break; case HEXAGON_NODETYPE: width = textSize.width + 2 * placer.hexagon[0]; height = textSize.height + 2 * placer.hexagon[1]; break; default: break; } deltaText.x = (width - textSize.width) / 2; deltaText.y = (height - textSize.height) / 2; deltaLink.x = width / 2; deltaLink.y = height / 2; NXSetRect(&textCellFrame, 0, 0, textSize.width, textSize.height); return self; } - internalCopyFromZone:(NXZone *)zone parent:aParent { Tree *theCopy; theCopy = [super copyFromZone:zone]; theCopy->contour.upper.head = contour.upper.tail = NULL; theCopy->contour.lower.head = contour.lower.tail = NULL; theCopy->updateLayout = YES; theCopy->parent = aParent; if(child) theCopy->child = [child internalCopyFromZone:zone parent:theCopy]; else theCopy->child = nil; if(sibling) theCopy->sibling = [sibling internalCopyFromZone:zone parent:aParent]; else theCopy->sibling = nil; theCopy->selected = NO; theCopy->gpath = [gpath copyFromZone:zone]; theCopy->nodeDescription = [nodeDescription copyFromZone:zone];; theCopy->delegate = nil; if(verknuepfung) theCopy->verknuepfung = [verknuepfung copyFromZone:zone]; else theCopy->verknuepfung = nil; theCopy->textCell = [textCell copyFromZone:zone]; return theCopy; } - calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint { NXPoint middle,l1,l2; float s,xm,ym,x0,y0,r,ausdruck,a,b; middle.x = pos.x + width / 2; middle.y = pos.y + height / 2; if(aPoint->y == middle.y){ *alpha = 0; ip->y = middle.y; if(aPoint->x > middle.x) ip->x = pos.x + width; else ip->x = pos.x; return self; } if(aPoint->x == middle.x){ ip->x = middle.x; if(aPoint->y > middle.y){ ip->y = pos.y + height; *alpha = -90; } else { ip->y = pos.y; *alpha = 90; } return self; } s = (aPoint->y - middle.y) / (aPoint->x - middle.x); *alpha = 57.29577951 * atan(s); if(aPoint->y > middle.y){ if(aPoint->x > middle.x){ // 4. quadrant switch(pathKind){ case RECT_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y + height; l2.x = pos.x + width; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } break; case ROUNDRECT_NODETYPE: l1.x = middle.x; l1.y = pos.y + height; l2.x = pos.x + width - height / 3; l2.y = pos.y + height; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = l2.x = pos.x + width; l1.y = middle.y; l2.y = pos.y + height - height / 3; if(straddles(&l1, &l2, &middle, aPoint)){ ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } else { r = height / 3; x0 = middle.x; y0 = middle.y; xm = pos.x + width - r; ym = pos.y + height - r; ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2)); ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2)); if(ip->y < ym){ ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2)); } } } break; case ELLIPSE_NODETYPE: x0 = middle.x; y0 = middle.y; a = width / 2; b = height / 2; ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0; ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; if(ip->y < y0){ ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0; ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; } break; case RHOMB_NODETYPE: l1.x = middle.x; l1.y = pos.y + height; l2.x = pos.x + width; l2.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); break; case HEXAGON_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y + height; l2.x = pos.x + width - placer.hexagon[0]; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = pos.x + width; l1.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); } break; default: break; } } else { // 3. quadrant switch(pathKind){ case RECT_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y + height; l2.x = pos.x; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } break; case ROUNDRECT_NODETYPE: l1.x = middle.x; l1.y = pos.y + height; l2.x = pos.x + height / 3; l2.y = pos.y + height; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = l2.x = pos.x; l1.y = middle.y; l2.y = pos.y + height - height / 3; if(straddles(&l1, &l2, &middle, aPoint)){ ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } else { r = height / 3; x0 = middle.x; y0 = middle.y; xm = pos.x + r; ym = pos.y + height - r; ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2)); ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2)); if(ip->y < ym){ ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2)); } } } break; case ELLIPSE_NODETYPE: x0 = middle.x; y0 = middle.y; a = width / 2; b = height / 2; ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0; ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; if(ip->y < y0){ ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0; ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; } break; case RHOMB_NODETYPE: l1.x = middle.x; l1.y = pos.y + height; l2.x = pos.x; l2.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); break; case HEXAGON_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y + height; l2.x = pos.x + placer.hexagon[0]; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = pos.x; l1.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); } break; default: break; } } } else { if(aPoint->x > middle.x){ // 1. quadrant switch(pathKind){ case RECT_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y; l2.x = pos.x + width; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } break; case ROUNDRECT_NODETYPE: l1.x = middle.x; l1.y = pos.y; l2.x = pos.x + width - height / 3; l2.y = pos.y; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = l2.x = pos.x + width; l1.y = middle.y; l2.y = pos.y + height / 3; if(straddles(&l1, &l2, &middle, aPoint)){ ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } else { r = height / 3; x0 = middle.x; y0 = middle.y; xm = pos.x + width - r; ym = pos.y + r; ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2)); ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2)); if(ip->y > ym){ ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2)); } } } break; case ELLIPSE_NODETYPE: x0 = middle.x; y0 = middle.y; a = width / 2; b = height / 2; ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0; ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; if(ip->y > y0){ ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0; ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; } break; case RHOMB_NODETYPE: l1.x = middle.x; l1.y = pos.y; l2.x = pos.x + width; l2.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); break; case HEXAGON_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y; l2.x = pos.x + width - placer.hexagon[0]; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = pos.x + width; l1.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); } break; default: break; } } else { // 2. quadrant switch(pathKind){ case RECT_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y; l2.x = pos.x; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } break; case ROUNDRECT_NODETYPE: l1.x = middle.x; l1.y = pos.y; l2.x = pos.x + height / 3; l2.y = pos.y; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = l2.x = pos.x; l1.y = middle.y; l2.y = pos.y + height / 3; if(straddles(&l1, &l2, &middle, aPoint)){ ip->x = l2.x; ip->y = middle.y + s * (ip->x - middle.x); } else { r = height / 3; x0 = middle.x; y0 = middle.y; xm = pos.x + r; ym = pos.y + r; ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2)); ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2)); if(ip->y > ym){ ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2)); ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2)); } } } break; case ELLIPSE_NODETYPE: x0 = middle.x; y0 = middle.y; a = width / 2; b = height / 2; ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0; ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; if(ip->y > y0){ ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0; ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2; } break; case RHOMB_NODETYPE: l1.x = middle.x; l1.y = pos.y; l2.x = pos.x; l2.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); break; case HEXAGON_NODETYPE: l1.x = middle.x; l1.y = l2.y = pos.y; l2.x = pos.x + placer.hexagon[0]; if(straddles(&l1, &l2, &middle, aPoint)){ ip->y = l1.y; ip->x = middle.x + (ip->y - middle.y) / s; } else { l1.x = pos.x; l1.y = middle.y; ausdruck = (l2.y - l1.y) / (l2.x - l1.x); ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s); ip->y = middle.y + s * (ip->x - middle.x); } break; default: break; } } } return self; } @end //************************************************************************ // static c functions implementation static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl, NXCoord *low, NXCoord *loh) { NXCoord minX, minY, maxX, maxY, x, y; Polyline *dummy; minX = minY = 9999; maxX = maxY = -9999; dummy = aPol->upper.head; x = ipu->x; y = ipu->y; minX = MIN(minX, x); minY = MIN(minY, y); maxX = MAX(maxX, x); maxY = MAX(maxY, y); while(dummy){ x += dummy->dx; y += dummy->dy; minX = MIN(minX, x); minY = MIN(minY, y); maxX = MAX(maxX, x); maxY = MAX(maxY, y); dummy = dummy->link; } dummy = aPol->lower.head; x = ipl->x; y = ipl->y; minX = MIN(minX, x); minY = MIN(minY, y); maxX = MAX(maxX, x); maxY = MAX(maxY, y); while(dummy){ x += dummy->dx; y += dummy->dy; minX = MIN(minX, x); minY = MIN(minY, y); maxX = MAX(maxX, x); maxY = MAX(maxY, y); dummy = dummy->link; } NXSetRect(rect, minX, minY, maxX - minX, maxY - minY); *loh = ipu->y - minY; *low = ipu->x - minX; } static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone) { NXCoord x,y,total,d; Polyline *lower,*upper,*b; x = y = total = 0; upper = c1->lower.head; lower = c2->upper.head; while(lower && upper){ d = offset(x,y,lower->dx,lower->dy,upper->dx,upper->dy); y += d; total += d; if(x + lower->dx <= upper->dx){ y += lower->dy; x += lower->dx; lower = lower->link; } else { y -= upper->dy; x -= upper->dx; upper = upper->link; } } if(lower){ b = bridge(c1->upper.tail,lower,0,0,x,y,zone); c1->upper.tail = (b->link) ? c2->upper.tail : b; c1->lower.tail = c2->lower.tail; partialFreePolyline(c2->upper.head, lower->link); freePolyline(c1->lower.head); } else { b = bridge(c2->lower.tail,upper,x,y,0,0,zone); if(!b->link) c1->lower.tail = b; partialFreePolyline(c1->lower.head,upper->link); freePolyline(c2->upper.head); } c1->lower.head = c2->lower.head; return total; } static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2, NXCoord b1,NXCoord b2) { NXCoord d,s,t; if(b1 <= p1 || p1 + a1 <= 0) return 0; t = b1 * a2 - a1 * b2; if(t > 0){ if(p1 < 0) s = p1 * a2,d = s / a1 - p2; else if(p1 > 0) s = p1 * b2,d = s / b1 - p2; else d = -p2; } else if(b1 < p1 + a1) s = (b1 - p1) * a2,d = b2 - (p2 + s / a1); else if(b1 > p1 + a1) s = (a1 + p1) * b2, d = s / b1 - (p2 + a2); else d = b2 - (p2 + a2); return MAX(0,d); } static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1, NXCoord x2,NXCoord y2, NXZone *zone) { NXCoord dx,dy,s; Polyline *r; dx = x2 + line2->dx - x1; if(line2->dx == 0) dy = line2->dy; else s = dx * line2->dy,dy = s / line2->dx; r = allocLine(dx,dy,line2->link,zone); line1->link = allocLine(0,y2 + line2->dy -dy - y1,r,zone); return r; } static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone) { Polyline *theAlloced; theAlloced = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline)); theAlloced->dx = aX; theAlloced->dy = aY; theAlloced->link = aLink; return theAlloced; } static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone) { Polyline *aLine,*l,*result; l = aSource; aLine = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline)); aLine->dx = l->dx; aLine->dy = l->dy; aLine->link = NULL; result = aLine; l = l->link; while(l){ aLine->link = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline)); aLine = aLine->link; aLine->dx = l->dx; aLine->dy = l->dy; aLine->link = NULL; l = l->link; } *aTail = aLine; return result; } static void freePolyline(Polyline *aLine) { Polyline *index,*trailer; trailer = aLine; index = aLine; while(index){ index = index->link; NX_FREE(trailer); trailer = index; } } static void partialFreePolyline(Polyline *aLine, Polyline *toEnd) { Polyline *index,*trailer; trailer = aLine; index = aLine; while(index && index != toEnd){ index = index->link; NX_FREE(trailer); trailer = index; } } static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4) { float crossP1, crossP2; crossP1 = (p3->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p3->y - p1->y); crossP2 = (p4->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p4->y - p1->y); if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0)){ crossP1 = (p1->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p1->y - p3->y); crossP2 = (p2->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p2->y - p3->y); if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0)) return YES; else return NO; } else return NO; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.