home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Misc / Wood.0.72 / Sources / Tree.m < prev    next >
Encoding:
Text File  |  1995-08-22  |  79.7 KB  |  3,075 lines

  1. #import <math.h>
  2. #import <appkit/workspaceRequest.h>
  3. #import <misckit/MiscString.h>
  4.  
  5. #import "PSWEnding.h"
  6. #import "PSWUControl.h"
  7. #import "RTFDescription.h"
  8. #import "UndoManager.h"
  9.  
  10. #import "Tree.h"
  11.  
  12. //************************************************************************
  13. //  static c functions declaration
  14.  
  15. // static functions for layouting
  16. static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl,
  17.                                 NXCoord *low, NXCoord *loh);
  18. static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone);
  19. static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2,
  20.                       NXCoord b1,NXCoord b2);
  21. static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1,
  22.                        NXCoord x2,NXCoord y2, NXZone *zone);
  23. static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone);
  24. static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone);
  25. static void freePolyline(Polyline *aLine);
  26. static void partialFreePolyline(Polyline *aLine, Polyline *toEnd);
  27.  
  28. // static functions for calculating path intersection
  29. static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4);
  30.  
  31. // static variables for general use 
  32. static float controlPts[8],controlX,controlY,officialFontSize;
  33. static char controlChars[5];
  34. static char controlPrimChars[5];
  35. static NXAtom fontname;
  36. static ButtonCell *attachCell, *soundCell; 
  37. static Cell *zippedCell;
  38. static NXRect attachCellFrame, soundCellFrame, zippedCellFrame;
  39. static UPath *arrowEnding, *doubleEnding, *circleEnding;
  40. static UPath *parentArrowEnding, *parentDoubleEnding, *parentCircleEnding;
  41.  
  42. // static variables for diagram2 streaming
  43. #import "DGVariables.h"
  44.  
  45. @interface Tree(PrivateMethods)
  46.  
  47. - attachParent:(NXCoord)h;
  48. - layoutLeaf;
  49. - (NXCoord)join;
  50. - makePath;
  51. - changeText;
  52. - changePos;
  53. - fillGPathParams;
  54. - calcWidthHeight;
  55. - internalCopyFromZone:(NXZone *)zone parent:aParent;
  56. - calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint;
  57.  
  58. @end
  59.  
  60. @implementation Tree
  61.  
  62. //************************************************************************
  63. // creating and destroying
  64.  
  65. + initialize
  66. {
  67.     int i;
  68.     
  69.     #ifndef FILTER
  70.     
  71.     NXRect dummy;
  72.     id dummyId;
  73.     
  74.     #endif
  75.     
  76.     if(self == [Tree class]){
  77.         [Tree setVersion:1];
  78.         fontname = NXUniqueString("ControlPoints");
  79.         PSWDefineFont(fontname);
  80.         officialFontSize = 8.0;
  81.         controlPts[6] = 0;
  82.         controlPts[7] = 0;
  83.         controlChars[4] = 0;
  84.         controlPrimChars[4] = 0;
  85.         for(i = 0;i < 4;i++){
  86.             controlChars[i] = 'a';
  87.             controlPrimChars[i] = 'e';
  88.         }
  89.         
  90.         #ifndef FILTER
  91.         
  92.         dummyId = [NXImage findImageNamed:"NXLinkButton"];
  93.         if([dummyId lockFocus])
  94.             [dummyId unlockFocus];
  95.         dummyId = [NXImage findImageNamed:"NXLinkButtonH"];
  96.         if([dummyId lockFocus])
  97.             [dummyId unlockFocus];
  98.         dummyId = [NXImage findImageNamed:"zipped"];
  99.         if([dummyId lockFocus])
  100.             [dummyId unlockFocus];
  101.         dummyId = [NXImage findImageNamed:"soundMarker"];
  102.         if([dummyId lockFocus])
  103.             [dummyId unlockFocus];
  104.         dummyId = [NXImage findImageNamed:"soundMarkerH"];
  105.         if([dummyId lockFocus])
  106.             [dummyId unlockFocus];
  107.         NXSetRect(&dummy, 0, 0, 100, 100);
  108.         attachCell = [[ButtonCell alloc] initIconCell:"NXLinkButton"];
  109.         [attachCell setAltIcon:"NXLinkButtonH"];
  110.         [attachCell setBordered:NO];
  111.         [attachCell setHighlightsBy:NX_CONTENTS];
  112.         [attachCell setIconPosition:NX_ICONONLY];
  113.         [attachCell calcCellSize:&(attachCellFrame.size) inRect:&dummy];
  114.         zippedCell = [[Cell alloc] initIconCell:"zipped"];
  115.         [zippedCell setBordered:NO];
  116.         [zippedCell calcCellSize:&(zippedCellFrame.size) inRect:&dummy];
  117.         soundCell = [[ButtonCell alloc] initIconCell:"soundMarker"];
  118.         [soundCell setAltIcon:"soundMarkerH"];
  119.         [soundCell setBordered:NO];
  120.         [soundCell setHighlightsBy:NX_CONTENTS];
  121.         [soundCell setIconPosition:NX_ICONONLY];
  122.         [soundCell calcCellSize:&(soundCellFrame.size) inRect:&dummy];
  123.         
  124.         #endif
  125.          
  126.         arrowEnding = [[UPath alloc] init];
  127.         [arrowEnding moveto:0 :0];
  128.         [arrowEnding rlineto:-10 :5];
  129.         [arrowEnding rlineto:3 :-5];
  130.         [arrowEnding rlineto:-3 :-5];
  131.         [arrowEnding closepath];
  132.         doubleEnding = [[UPath alloc] init];
  133.         [doubleEnding moveto:0 :0];
  134.         [doubleEnding rlineto:-5 :5];
  135.         [doubleEnding rlineto:2 :-5];
  136.         [doubleEnding rlineto:-7 :5];
  137.         [doubleEnding rlineto:2 :-5];
  138.         [doubleEnding rlineto:-2 :-5];
  139.         [doubleEnding rlineto:7 :5];
  140.         [doubleEnding rlineto:-2 :-5];
  141.         [doubleEnding closepath];
  142.         circleEnding = [[UPath alloc] init];
  143.         [circleEnding moveto:0 :0];
  144.         [circleEnding arc:-3 :0 :3 :0 :360];
  145.         parentArrowEnding = [[UPath alloc] init];
  146.         [parentArrowEnding moveto:0 :0];
  147.         [parentArrowEnding rlineto:10 :5];
  148.         [parentArrowEnding rlineto:-3 :-5];
  149.         [parentArrowEnding rlineto:3 :-5];
  150.         [parentArrowEnding closepath];
  151.         parentDoubleEnding = [[UPath alloc] init];
  152.         [parentDoubleEnding moveto:0 :0];
  153.         [parentDoubleEnding rlineto:5 :5];
  154.         [parentDoubleEnding rlineto:-2 :-5];
  155.         [parentDoubleEnding rlineto:7 :5];
  156.         [parentDoubleEnding rlineto:-2 :-5];
  157.         [parentDoubleEnding rlineto:2 :-5];
  158.         [parentDoubleEnding rlineto:-7 :5];
  159.         [parentDoubleEnding rlineto:2 :-5];
  160.         [parentDoubleEnding closepath];
  161.         parentCircleEnding = [[UPath alloc] init];
  162.         [parentCircleEnding moveto:6 :0];
  163.         [parentCircleEnding arc:3 :0 :3 :0 :360];
  164.     }
  165.     return self;
  166. }
  167.  
  168. - initLabel:(const char *)aLabel props:(Properties *)aProps
  169. {  
  170.        [super init];
  171.     contour.lower.head = contour.upper.head = NULL;
  172.        contour.lower.tail = contour.upper.tail = NULL;
  173.     parent = child = sibling = nil;
  174.     delegate = nil;
  175.     hasVerknuepfung = NO;
  176.     verknuepfung = nil;
  177.     hasSound = NO;
  178.     geraeusch = nil;
  179.     offset.x = offset.y = 0.0;
  180.     pos.x = pos.y = 0.0; 
  181.     selected = NO;
  182.     updateLayout = YES;
  183.     if(aLabel)
  184.         textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aLabel];
  185.     else
  186.         textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aProps->defaultNodeName];
  187.     [textCell setBordered:NO];
  188.     [textCell setBackgroundTransparent:YES];
  189.     [textCell setTextColor:aProps->textColor];
  190.     [textCell setAlignment:NX_CENTERED];
  191.     nodeDescription = [[RTFDescription allocFromZone:[self zone]] init];
  192.     zipped = aProps->zipped;        
  193.     border = aProps->border;
  194.     shadow = aProps->shadow;
  195.     fill = aProps->fill;
  196.     outline = aProps->outline;
  197.     linkKind = aProps->linkKind;
  198.     biegFactor = aProps->biegFactor;
  199.     parentDistance = aProps->parentDistance;
  200.     fillColor = aProps->fillColor;
  201.     outlineColor = aProps->outlineColor;
  202.     shadowColor = aProps->shadowColor;
  203.     ending = aProps->ending;
  204.     parentEnding = aProps->parentEnding;
  205.     gpath = [[UPath allocFromZone:[self zone]] init];
  206.     pathKind = aProps->pathKind;
  207.     deltaShadow.x = deltaShadow.y = 3;
  208.     placer.rect[0] = placer.rect[1] = 6;
  209.     placer.roundRect[0] = placer.roundRect[1] = 6;
  210.     placer.roundRect[2] = 3;
  211.     placer.ellipse[0] = 6;
  212.     placer.ellipse[1] = 4;
  213.     placer.rhomb[0] = 12;
  214.     placer.rhomb[1] = 3;
  215.     placer.hexagon[0] = 6;
  216.     placer.hexagon[1] = 6;
  217.     placer.hexagon[2] = 1;
  218.     [self makePath];     
  219.        return self;
  220. }
  221.    
  222. - free
  223. {
  224.     [nodeDescription free];
  225.     if(contour.lower.head)
  226.         freePolyline(contour.lower.head);
  227.     if(contour.upper.head)
  228.         freePolyline(contour.upper.head);
  229.     if(child)
  230.         [child free];
  231.     if(sibling)
  232.         [sibling free];
  233.     [gpath free];
  234.     if(verknuepfung)
  235.         [verknuepfung free];
  236.     if(geraeusch)
  237.         [geraeusch free];
  238.     [textCell free];
  239.        return [super free];
  240. }
  241.  
  242. - copyFromZone:(NXZone *)zone
  243. {
  244.        Tree *theCopy;
  245.  
  246.        theCopy = [super copyFromZone:zone];
  247.     theCopy->contour.upper.head = contour.upper.tail = NULL;
  248.     theCopy->contour.lower.head = contour.lower.tail = NULL;
  249.     theCopy->updateLayout = YES;
  250.     theCopy->parent = nil;
  251.     theCopy->sibling = nil;
  252.     if(child)
  253.         theCopy->child = [child internalCopyFromZone:zone parent:theCopy];
  254.     else
  255.         theCopy->child = nil;
  256.     theCopy->selected = NO;
  257.     theCopy->gpath = [gpath copyFromZone:zone];
  258.     theCopy->nodeDescription = [nodeDescription copyFromZone:zone];
  259.     theCopy->delegate = nil;
  260.     if(verknuepfung)
  261.         theCopy->verknuepfung = [verknuepfung copyFromZone:zone];
  262.     else
  263.         theCopy->verknuepfung = nil;
  264.     if(geraeusch)
  265.         theCopy->geraeusch = [geraeusch copyFromZone:zone];
  266.     else
  267.         theCopy->geraeusch = nil;
  268.     theCopy->textCell = [textCell copyFromZone:zone];
  269.        return theCopy;
  270. }
  271.  
  272. //************************************************************************
  273. // layout and drawing
  274.  
  275. - involved:(const NXRect *)aRect addTo:aList link:aPath
  276. {
  277.     NXRect rect,dummyRect;
  278.     NXPoint point,mp,parentPoint,childPoint;
  279.     NXCoord dist;
  280.  
  281.     if(parent && parent->zipped)
  282.         return self;
  283.     [self getBounds:&rect];
  284.     if(aRect == NULL || NXIntersectsRect(&rect,aRect))
  285.         [aList addObject:self];
  286.     if(parent){
  287.         [parent linkPoint:&point];
  288.         [self linkPoint:&mp];
  289.         if(linkKind){
  290.             [parent linkParentPoint:&parentPoint];
  291.             [self linkChildPoint:&childPoint];
  292.         }    
  293.         dist = ABS(point.y - mp.y);
  294.         if(dist > 0)
  295.             NXSetRect(&dummyRect,MIN(mp.x,point.x) - 0.5,MIN(mp.y,point.y) - 0.5,
  296.                     ABS(point.x - mp.x) + 1,dist + 1);
  297.         else
  298.             NXSetRect(&dummyRect,MIN(mp.x,point.x),mp.y - 0.5,
  299.                     ABS(point.x - mp.x),1.0);
  300.         if(aRect == NULL || NXIntersectsRect(aRect,&dummyRect)){
  301.             if(linkKind == 0){
  302.                 [aPath moveto:point.x :point.y];
  303.                 [aPath lineto:mp.x :mp.y];
  304.             } else {
  305.                 [aPath moveto:point.x :point.y];
  306.                 [aPath lineto:parentPoint.x :parentPoint.y];
  307.                 [aPath lineto:childPoint.x :childPoint.y];
  308.                 [aPath lineto:mp.x :mp.y];
  309.             }
  310.         }
  311.     }
  312.     if(child)
  313.         [child involved:aRect addTo:aList link:aPath];
  314.     if(sibling)
  315.         [sibling involved:aRect addTo:aList link:aPath];
  316.     return self;
  317. }
  318.  
  319. - getBounds:(NXRect *)rect
  320. {
  321.     NXRect dummy;
  322.     NXCoord dxy, dwh;
  323.     
  324.     NXSetRect(rect,pos.x,pos.y,width,height);
  325.     if(selected){
  326.         dxy = MAX(5, linewidth);
  327.         dwh = MAX(10, linewidth);    
  328.         rect->origin.x -= dxy;
  329.         rect->origin.y -= dxy;
  330.         rect->size.width += dwh;
  331.         rect->size.height += dwh;
  332.     }
  333.     if(shadow){
  334.         rect->size.width += deltaShadow.x;
  335.         rect->size.height += deltaShadow.y;
  336.     }
  337.     if((ending && parent) || (parentEnding && child)){
  338.         NXInsetRect(rect, -15, -15);
  339.     }
  340.     if(hasVerknuepfung){
  341.         NXSetRect(&dummy, pos.x + 23, 
  342.             pos.y - linewidth - attachCellFrame.size.height - 1, 
  343.             attachCellFrame.size.width, 
  344.             attachCellFrame.size.height);
  345.         NXUnionRect(&dummy, rect);
  346.     }
  347.     if(zipped){
  348.         NXSetRect(&dummy, pos.x + 7, 
  349.             pos.y - linewidth - zippedCellFrame.size.height - 1, 
  350.             zippedCellFrame.size.width, 
  351.             zippedCellFrame.size.height);
  352.         NXUnionRect(&dummy, rect);
  353.     }
  354.     if(hasSound){
  355.         NXSetRect(&dummy, pos.x + 37, 
  356.             pos.y - linewidth - soundCellFrame.size.height - 1, 
  357.             soundCellFrame.size.width, 
  358.             soundCellFrame.size.height);
  359.         NXUnionRect(&dummy, rect);
  360.     }
  361.     return self;
  362. }
  363.  
  364. - getTreeBounds:(NXRect *)rect lowerWidth:(NXCoord *)lowW lowerHeight:(NXCoord *)lowH
  365. {
  366.     NXPoint p1,p2;
  367.     
  368.     p1.x = pos.x - border;
  369.     p1.y = pos.y - border;
  370.     p2.x = pos.x - border;
  371.     p2.y = pos.y + height + border;
  372.     setRectFromPolygon(rect, &contour, &p1, &p2, lowW, lowH);
  373.     NXInsetRect(rect,border - linewidth,border - linewidth);
  374.     if(shadow){
  375.         rect->size.width += deltaShadow.x;
  376.         rect->size.height += deltaShadow.y;
  377.     }
  378.     return self;
  379. }
  380.  
  381. - hit:(HitPath *)hitUPath
  382. {
  383.     BOOL didHit;
  384.     id dummy;
  385.  
  386.     didHit = [hitUPath hitPathFill:gpath];
  387.     if(didHit)
  388.         return self;
  389.     else if(child){
  390.         dummy = [child hit:hitUPath];
  391.         if(dummy)
  392.             return dummy;
  393.         else if(sibling)
  394.             return [sibling hit:hitUPath];
  395.     } else if(sibling)
  396.         return [sibling hit:hitUPath];
  397.     return nil;    
  398. }
  399.  
  400. - hitAttachment:(const NXPoint *)aPoint :(BOOL *)theAttach
  401. {
  402.     BOOL didHit;
  403.     id dummy;
  404.     NXRect dummyRect;
  405.  
  406.     *theAttach = NO;
  407.     if(hasVerknuepfung){
  408.         NXSetRect(&dummyRect, 
  409.             pos.x + 23, 
  410.             pos.y - linewidth - attachCellFrame.size.height - 1, 
  411.             attachCellFrame.size.width, 
  412.             attachCellFrame.size.height);
  413.         didHit = NXPointInRect(aPoint, &dummyRect);
  414.     } else
  415.         didHit = NO;
  416.     if(didHit){
  417.         *theAttach = YES;
  418.         return self;
  419.     }
  420.     if(hasSound){
  421.         NXSetRect(&dummyRect, 
  422.             pos.x + 37, 
  423.             pos.y - linewidth - soundCellFrame.size.height - 1, 
  424.             soundCellFrame.size.width, 
  425.             soundCellFrame.size.height);
  426.         didHit = NXPointInRect(aPoint, &dummyRect);
  427.     } else
  428.         didHit = NO;
  429.     if(didHit)
  430.         return self;
  431.     if(child){
  432.         dummy = [child hitAttachment:aPoint :theAttach];
  433.         if(dummy)
  434.             return dummy;
  435.         else if(sibling)
  436.             return [sibling hitAttachment:aPoint :theAttach];
  437.     } else if(sibling)
  438.         return [sibling hitAttachment:aPoint :theAttach];
  439.     return nil;    
  440. }
  441.  
  442. - activateAttachment:(NXEvent *)event inView:aView
  443. {
  444.     NXPoint p;
  445.     NXRect dummyRect;
  446.     id result;
  447.     
  448.     if(!hasVerknuepfung)
  449.         return self;
  450.     attachCellFrame.origin.x = pos.x + 23;
  451.     attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1;
  452.     [attachCell highlight:&attachCellFrame inView:aView lit:YES];
  453.     [[aView window] flushWindow];
  454.     while(event->type != NX_MOUSEUP)
  455.         event = [NXApp getNextEvent:NX_MOUSEUPMASK];
  456.     p = event->location; 
  457.     [aView convertPoint:&p fromView:nil];
  458.     dummyRect = attachCellFrame;
  459.     if(NXPointInRect(&p, &dummyRect)){
  460.         [[Application workspace] openFile:[verknuepfung sourceFilename]];
  461.         result = self;
  462.     } else
  463.         result = nil;
  464.     [attachCell highlight:&attachCellFrame inView:aView lit:NO];
  465.     [[aView window] flushWindow];
  466.     return result;
  467. }
  468.  
  469. - activateSound:(NXEvent *)event inView:aView
  470. {
  471.     NXPoint p;
  472.     NXRect dummyRect;
  473.     id result;
  474.     
  475.     if(!hasSound)
  476.         return self;
  477.     soundCellFrame.origin.x = pos.x + 37;
  478.         soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1;
  479.     [soundCell highlight:&soundCellFrame inView:aView lit:YES];
  480.     [[aView window] flushWindow];
  481.     while(event->type != NX_MOUSEUP)
  482.         event = [NXApp getNextEvent:NX_MOUSEUPMASK];
  483.     p = event->location; 
  484.     [aView convertPoint:&p fromView:nil];
  485.     dummyRect = soundCellFrame;
  486.     if(NXPointInRect(&p, &dummyRect)){
  487.         [geraeusch play];
  488.         result = self;
  489.     } else
  490.         result = nil;
  491.     [soundCell highlight:&soundCellFrame inView:aView lit:NO];
  492.     [[aView window] flushWindow];
  493.     return result;
  494. }
  495.  
  496. - drawNodeShadow:(DrawState *)aState
  497. {
  498.     NXPoint dummy;
  499.  
  500.     if(shadow){
  501.         if(!NXEqualColor(aState->color,shadowColor)){
  502.             aState->color = shadowColor;
  503.             NXSetColor(shadowColor);
  504.         }    
  505.         dummy = pos;
  506.         pos.x += deltaShadow.x;
  507.         pos.y += deltaShadow.y;
  508.         [self changePos];
  509.         [gpath send:dps_ufill cached:YES];
  510.         pos = dummy;
  511.         [self changePos];
  512.     }
  513.     return self;
  514. }
  515.  
  516. - drawNodeFill:(DrawState *)aState
  517. {
  518.     if(fill){
  519.         if(!NXEqualColor(aState->color,fillColor)){
  520.             aState->color = fillColor;
  521.             NXSetColor(fillColor);
  522.         }
  523.         [gpath send:dps_ufill cached:YES];
  524.     }
  525.     return self;
  526. }
  527.  
  528. - drawNodeEnding:(DrawState *)aState
  529. {
  530.     NXPoint outPoint,iPoint;
  531.     float rotAngle;
  532.     Tree *ptr;
  533.     
  534.     if(ending != ENDING_NONE && parent){
  535.         if(linkKind)
  536.             [self linkChildPoint:&outPoint];
  537.         else 
  538.             [parent linkPoint:&outPoint];
  539.         if(!NXEqualColor(aState->color,NX_COLORBLACK)){
  540.             aState->color = NX_COLORBLACK;
  541.             NXSetColor(NX_COLORBLACK);
  542.         }
  543.         if(aState->linewidth != 0.15){
  544.             aState->linewidth = 0.15;
  545.             PSsetlinewidth(0.15);
  546.         }
  547.         [self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
  548.         switch(ending){
  549.             case ENDING_ARROW:
  550.                 PSWEndingSolid(arrowEnding->bboxParams, arrowEnding->sizeParams + 4,
  551.                         arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  552.             break;
  553.             case ENDING_HOLLOW:
  554.                 PSWEndingHollow(arrowEnding->bboxParams, arrowEnding->sizeParams + 4,
  555.                         arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  556.             break;
  557.             case ENDING_DOUBLE:
  558.                 PSWEndingSolid(doubleEnding->bboxParams, doubleEnding->sizeParams + 4,
  559.                         doubleEnding->bboxOps, doubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  560.             break;
  561.             case ENDING_SOLID:
  562.                 PSWEndingSolid(circleEnding->bboxParams, circleEnding->sizeParams + 4,
  563.                         circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  564.             break;
  565.             case ENDING_CIRCLE:
  566.                 PSWEndingHollow(circleEnding->bboxParams, circleEnding->sizeParams + 4,
  567.                         circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  568.             break;
  569.             default:
  570.             break;
  571.         }
  572.     }
  573.     if(parentEnding != ENDING_NONE && child){
  574.         if(linkKind){
  575.             [self linkParentPoint:&outPoint];
  576.             [self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
  577.             switch(parentEnding){
  578.                 case ENDING_ARROW:
  579.                     PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
  580.                         parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  581.                 break;
  582.                 case ENDING_HOLLOW:
  583.                     PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
  584.                         parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  585.                 break;
  586.                 case ENDING_DOUBLE:
  587.                     PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4,
  588.                         parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  589.                 break;
  590.                 case ENDING_SOLID:
  591.                     PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
  592.                         parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  593.                 break;
  594.                 case ENDING_CIRCLE:
  595.                     PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
  596.                         parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  597.                 break;
  598.                 default:
  599.                 break;
  600.             }
  601.         } else {
  602.             for(ptr = child;ptr;ptr = ptr->sibling){
  603.                 [ptr linkPoint:&outPoint];
  604.                 [self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
  605.                 switch(parentEnding){
  606.                     case ENDING_ARROW:
  607.                         PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
  608.                             parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  609.                     break;
  610.                     case ENDING_HOLLOW:
  611.                         PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
  612.                             parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  613.                     break;
  614.                     case ENDING_DOUBLE:
  615.                         PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4,
  616.                             parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  617.                     break;
  618.                     case ENDING_SOLID:
  619.                         PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
  620.                             parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  621.                     break;
  622.                     case ENDING_CIRCLE:
  623.                         PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
  624.                             parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
  625.                     break;
  626.                     default:
  627.                     break;
  628.                 }    
  629.             }
  630.         }
  631.     }
  632.     return self;
  633. }
  634.  
  635. - drawNodeOutline:(DrawState *)aState knobs:(BOOL)aBool
  636. {
  637.     if(outline){
  638.         if(!NXEqualColor(aState->color,outlineColor)){
  639.             aState->color = outlineColor;
  640.             NXSetColor(outlineColor);
  641.         }
  642.         if(aState->linewidth != linewidth){
  643.             aState->linewidth = linewidth;
  644.             PSsetlinewidth(linewidth);
  645.         }
  646.         [gpath send:dps_ustroke cached:YES];
  647.     }
  648.     if(selected && aBool){
  649.         if(delegate)
  650.             PSselectfont(fontname,officialFontSize / [delegate docScale]);
  651.         else 
  652.             PSselectfont(fontname,officialFontSize);
  653.         aState->font = nil;
  654.         if(!NXEqualColor(aState->color,NX_COLORBLACK)){
  655.             aState->color = NX_COLORBLACK;
  656.             NXSetColor(NX_COLORBLACK);
  657.         }
  658.         controlX = gpath->bbox[0];
  659.         controlY = gpath->bbox[1];
  660.         controlPts[0] = gpath->bbox[2] - gpath->bbox[0];
  661.         controlPts[1] = 0;
  662.         controlPts[2] = 0; 
  663.         controlPts[3] = gpath->bbox[3] - gpath->bbox[1];
  664.         controlPts[4] = -controlPts[0];
  665.         controlPts[5] = 0;
  666.         PSWDrawControlPoints(controlX,controlY,controlPts,8,controlChars);
  667.         if(!NXEqualColor(aState->color,NX_COLORDKGRAY)){
  668.             aState->color = NX_COLORDKGRAY;
  669.             NXSetColor(NX_COLORDKGRAY);
  670.         }
  671.         PSWDrawControlPoints(controlX,controlY,controlPts,8,controlPrimChars);
  672.     }
  673.     return self;
  674. }
  675.  
  676. - drawNodeCellsInView:aView attachments:(BOOL)aBool
  677. {    
  678.     if(hasVerknuepfung && aBool){
  679.         attachCellFrame.origin.x = pos.x + 23;
  680.         attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1;
  681.         [attachCell drawSelf:&attachCellFrame inView:aView];
  682.     }
  683.     if(hasSound && aBool){
  684.         soundCellFrame.origin.x = pos.x + 37;
  685.         soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1;
  686.         [soundCell drawSelf:&soundCellFrame inView:aView];
  687.     }
  688.     textCellFrame.origin.x = pos.x + deltaText.x;
  689.     textCellFrame.origin.y = pos.y + deltaText.y;
  690.     [textCell drawSelf:&textCellFrame inView:aView];
  691.     if(aBool && zipped){
  692.         zippedCellFrame.origin.x = pos.x + 7;
  693.         zippedCellFrame.origin.y = pos.y - linewidth - zippedCellFrame.size.height - 1;
  694.         [zippedCell drawSelf:&zippedCellFrame inView:aView];
  695.     }
  696.     return self;    
  697. }
  698.  
  699. - setOffset:(NXCoord)aX :(NXCoord)aY
  700. {
  701.        offset.x = aX;
  702.        offset.y = aY;
  703.        return self;
  704. }
  705.  
  706. - (NXCoord)width
  707. {
  708.        return width;
  709. }
  710.  
  711. - (NXCoord)height
  712. {
  713.        return height;
  714. }
  715.  
  716. - (Polygon *)contour
  717. {
  718.        return &contour;
  719. }
  720.  
  721. - layout
  722. {
  723.     Tree *c;
  724.      
  725.     if(!updateLayout)
  726.         return nil;
  727.     if(zipped){
  728.         [self layoutLeaf];
  729.         updateLayout = NO;
  730.         return self;
  731.     }
  732.     for(c = child;c;c = [c sibling])
  733.           [c layout];
  734.     if(child)
  735.           [self attachParent:[self join]];
  736.     else
  737.           [self layoutLeaf];
  738.     updateLayout = NO;
  739.     return self;
  740. }
  741.  
  742. - getTextFrame:(NXRect *)aRect
  743. {
  744.     NXSetRect(aRect, pos.x, pos.y + deltaText.y, width, textCellFrame.size.height);
  745.     return self;
  746. }
  747.  
  748. //************************************************************************
  749. // tree data structure manipulation
  750.  
  751. - sibling
  752. {
  753.        return sibling;
  754. }
  755.  
  756. - child
  757. {
  758.     return child;
  759. }
  760.  
  761. - parent
  762. {
  763.     return parent;
  764. }
  765.  
  766. - addTree:(Tree *)aTree
  767. {
  768.     Tree *dummy;
  769.     id undoManager;
  770.     
  771.     aTree->parent = self;
  772.     aTree->linkKind = linkKind;
  773.     aTree->biegFactor = biegFactor;
  774.     aTree->parentDistance = parentDistance;
  775.     aTree->shadow = shadow;
  776.     aTree->shadowColor = shadowColor;
  777.     aTree->border = border;
  778.     if(!child){
  779.         child = aTree;
  780.     } else {
  781.         dummy = child;
  782.         while(dummy->sibling)
  783.             dummy = dummy->sibling;
  784.         dummy->sibling = aTree;
  785.         aTree->sibling = nil;
  786.     }
  787.     [self setUpdateLayout:YES];
  788.     if(delegate){
  789.         undoManager = [delegate undoManager];
  790.         [undoManager setUndoName:"Addition"];
  791.         [undoManager setRedoName:"Deletion"];
  792.         [[undoManager setUndoTarget:self] removeChild:aTree];
  793.         [delegate declareSelection:aTree];
  794.         [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)];
  795.     }
  796.     return self;
  797. }
  798.  
  799. - removeChild:(Tree *)aChild
  800. {
  801.     Tree *dummy;
  802.     id undoManager;
  803.     int i;
  804.     
  805.     if(!child || !aChild)
  806.         return self;
  807.     [self setUpdateLayout:YES];
  808.     dummy = child;
  809.     i = 0;
  810.     if(dummy == aChild)
  811.         child = aChild->sibling;
  812.     else {
  813.         while(dummy->sibling && dummy->sibling != aChild){
  814.             dummy = dummy->sibling;
  815.             i++;
  816.         }
  817.         if(dummy->sibling == aChild)
  818.             dummy->sibling = aChild->sibling;
  819.     }
  820.     aChild->parent = nil;
  821.     aChild->sibling = nil;
  822.     if(delegate){
  823.         [delegate declareSelection:nil];
  824.         undoManager = [delegate undoManager];
  825.         [undoManager freeUndoArgsOnRecordDiscard];
  826.         [undoManager setUndoName:"Deletion"];
  827.         [undoManager setRedoName:"Addition"];
  828.         [[undoManager setUndoTarget:self] insertTree:aChild at:i];
  829.         [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_ALL];
  830.     } else
  831.         [aChild free];
  832.     return self;
  833. }
  834.  
  835. - insertTree:(Tree *)aTree at:(int)aPos
  836. {
  837.     Tree *dummy;
  838.     id undoManager;
  839.     int i;
  840.     
  841.     aTree->parent = self;
  842.     aTree->linkKind = linkKind;
  843.     aTree->biegFactor = biegFactor;
  844.     aTree->parentDistance = parentDistance;
  845.     aTree->shadow = shadow;
  846.     aTree->shadowColor = shadowColor;
  847.     aTree->border = border;
  848.     if(!child){
  849.         child = aTree;
  850.     } else {
  851.         dummy = child;
  852.         i = 0;
  853.         while(dummy->sibling && i < aPos)
  854.             dummy = dummy->sibling;
  855.         aTree->sibling = dummy->sibling;
  856.         dummy->sibling = aTree;
  857.     }
  858.     [self setUpdateLayout:YES];
  859.     if(delegate){
  860.         undoManager = [delegate undoManager];
  861.         [undoManager setUndoName:"Addition"];
  862.         [undoManager setRedoName:"Deletion"];
  863.         [[undoManager setUndoTarget:self] removeChild:aTree];
  864.         [delegate declareSelection:aTree];
  865.         [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)];
  866.     }
  867.     return self;
  868. }
  869.  
  870.     
  871. //************************************************************************
  872. // properties
  873.  
  874. - attachFile:(const char *)aFileName
  875. {
  876.     NXRect r;
  877.     BOOL attachUpdate = YES;
  878.     
  879.     if(verknuepfung){
  880.         [verknuepfung free];
  881.         attachUpdate = NO;
  882.     }
  883.     verknuepfung = [[NXDataLink allocFromZone:[self zone]] initLinkedToFile:aFileName];
  884.     if(attachUpdate){
  885.         hasVerknuepfung = YES;
  886.         [self getBounds:&r];
  887.         [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  888.     }
  889.     return self;
  890. }
  891.  
  892. - breakAttachment
  893. {
  894.     NXRect r;
  895.     
  896.     if(!hasVerknuepfung)
  897.         return self;
  898.     [self getBounds:&r];
  899.     hasVerknuepfung = NO; 
  900.     [verknuepfung free];
  901.     verknuepfung = nil;
  902.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  903.     return self;
  904. }
  905.  
  906. - (BOOL)attached
  907. {
  908.     return hasVerknuepfung;
  909. }
  910.  
  911. - attachSound:(const char *)aSoundName
  912. {
  913.     NXRect r;
  914.     BOOL soundUpdate = YES;
  915.     
  916.     if(geraeusch){
  917.         [geraeusch free];
  918.         soundUpdate = NO;
  919.     }
  920.     geraeusch = [[Sound allocFromZone:[self zone]] initFromSoundfile:aSoundName];
  921.     if(soundUpdate){
  922.         hasSound = YES;
  923.         [self getBounds:&r];
  924.         [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  925.     }
  926.     return self;
  927. }
  928.  
  929. - breakSound
  930. {
  931.     NXRect r;
  932.     
  933.     if(!hasSound)
  934.         return self;
  935.     [self getBounds:&r];
  936.     hasSound = NO; 
  937.     [geraeusch free];
  938.     geraeusch = nil;
  939.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  940.     return self;
  941. }
  942.  
  943. - (BOOL)hasSound
  944. {
  945.     return hasSound;
  946. }
  947.  
  948. - attachedSound
  949. {
  950.     return geraeusch;
  951. }
  952.  
  953. - setSound:aSound
  954. {
  955.     NXRect r;
  956.     BOOL soundUpdate = YES;
  957.     
  958.     if(geraeusch){
  959.         [geraeusch free];
  960.         soundUpdate = NO;
  961.     }
  962.     geraeusch = aSound;
  963.     if(soundUpdate){
  964.         hasSound = YES;
  965.         [self getBounds:&r];
  966.         [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  967.     }
  968.     return self;
  969. }
  970.  
  971. - applyStyleToSubtree
  972. {
  973.     Properties props;
  974.     
  975.     if(child){
  976.         [self getProps:&props];
  977.         [child promoteProps:&props];
  978.         [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  979.     }
  980.     return self;
  981. }
  982.  
  983. - promoteProps:(Properties *)aProps
  984. {
  985.     outline = aProps->outline;
  986.     if([textCell font] != aProps->font){
  987.         [textCell setFont:aProps->font];
  988.         [self changeText];
  989.         [self setUpdateLayout:YES];
  990.     }
  991.     if(pathKind != aProps->pathKind){
  992.         [gpath resetFill];
  993.         pathKind = aProps->pathKind;
  994.         [self makePath];
  995.         [self setUpdateLayout:YES];
  996.     }
  997.     fillColor = aProps->fillColor;
  998.     outlineColor = aProps->outlineColor;
  999.     [textCell setTextColor:aProps->textColor];
  1000.     linewidth = aProps->linewidth;
  1001.     if(ending != aProps->ending){
  1002.         ending = aProps->ending;
  1003.     }
  1004.     if(parentEnding != aProps->parentEnding){
  1005.         parentEnding = aProps->parentEnding;
  1006.     }
  1007.     if(sibling)
  1008.         [sibling promoteProps:aProps];
  1009.     if(child)
  1010.         [child promoteProps:aProps];
  1011.     return self;
  1012. }
  1013.  
  1014. - getProps:(Properties *)aProps
  1015. {
  1016.     aProps->outline = outline;
  1017.     aProps->font = [textCell font];
  1018.     aProps->pathKind = pathKind;
  1019.     aProps->fillColor = fillColor;
  1020.     aProps->outlineColor = outlineColor;
  1021.     aProps->textColor = [textCell textColor];
  1022.     aProps->linewidth = linewidth;
  1023.     aProps->ending = ending;
  1024.     aProps->parentEnding = parentEnding;
  1025.     return self;
  1026. }
  1027.  
  1028. - changeTo:(Properties *)aProps
  1029. {
  1030.     NXRect r, dummyRect, pr;
  1031.     BOOL checkResize, makeUnion;
  1032.     
  1033.     [self getBounds:&r];
  1034.     checkResize = NO;
  1035.     makeUnion = NO;
  1036.     outline = aProps->outline;
  1037.     if([textCell font] != aProps->font){
  1038.         [textCell setFont:aProps->font];
  1039.         [self changeText];
  1040.         [self setUpdateLayout:YES];
  1041.         checkResize = YES;
  1042.     }
  1043.     if(pathKind != aProps->pathKind){
  1044.         [gpath resetFill];
  1045.         pathKind = aProps->pathKind;
  1046.         [self makePath];
  1047.         [self setUpdateLayout:YES];
  1048.         checkResize = YES;
  1049.     }
  1050.     fillColor = aProps->fillColor;
  1051.     outlineColor = aProps->outlineColor;
  1052.     [textCell setTextColor:aProps->textColor];
  1053.     if(ending != aProps->ending){
  1054.         ending = aProps->ending;
  1055.         makeUnion = YES;
  1056.     }
  1057.     if(parentEnding != aProps->parentEnding){
  1058.         parentEnding = aProps->parentEnding;
  1059.         if(parent){
  1060.             [parent getBounds:&pr];
  1061.             [delegate updateViewsDirty:YES rect:&pr flag:UPDATE_TREEVIEW];
  1062.         }
  1063.     }
  1064.     if(aProps->linewidth != linewidth){
  1065.         linewidth = aProps->linewidth;
  1066.         makeUnion = YES;
  1067.     }
  1068.     if(makeUnion){
  1069.         [self getBounds:&dummyRect];
  1070.         NXUnionRect(&dummyRect, &r);
  1071.     }
  1072.     if(checkResize)
  1073.         [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1074.     else
  1075.         [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  1076.     return self;
  1077. }
  1078.     
  1079. //************************************************************************
  1080. // tree properties
  1081.  
  1082. - (NXCoord)border
  1083. {
  1084.        return border;
  1085. }
  1086.  
  1087. - setBorder:(NXCoord)aBorder
  1088. {
  1089.     border = aBorder;
  1090.     [self setUpdateLayout:YES];
  1091.     if(child)
  1092.         [child setBorder:aBorder];
  1093.     if(sibling)
  1094.         [sibling setBorder:aBorder];
  1095.     if(!parent)
  1096.         [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1097.     return self;
  1098. }
  1099.  
  1100. - (NXCoord)parentDistance
  1101. {
  1102.     return parentDistance;
  1103. }
  1104.  
  1105. - setParentDistance:(NXCoord)aParentDistance
  1106. {
  1107.     parentDistance = aParentDistance;
  1108.     [self setUpdateLayout:YES];
  1109.     if(child)
  1110.         [child setParentDistance:aParentDistance];
  1111.     if(sibling)
  1112.         [sibling setParentDistance:aParentDistance];
  1113.     if(!parent)
  1114.         [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1115.     return self;
  1116. }
  1117.  
  1118. - (BOOL)shadow
  1119. {
  1120.     return shadow;
  1121. }
  1122.  
  1123. - setShadow:(BOOL)aShadow
  1124. {    
  1125.     shadow = aShadow;
  1126.     if(child)
  1127.         [child setShadow:aShadow];
  1128.     if(sibling)
  1129.         [sibling setShadow:aShadow];
  1130.     if(!parent)
  1131.         [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1132.     return self;
  1133. }
  1134.  
  1135. - (NXColor)shadowColor
  1136. {
  1137.     return shadowColor;
  1138. }
  1139.  
  1140. - setShadowColor:(NXColor)aColor
  1141. {    
  1142.     shadowColor = aColor;
  1143.     if(child)
  1144.         [child setShadowColor:aColor];
  1145.     if(sibling)
  1146.         [sibling setShadowColor:aColor];
  1147.     if(!parent)
  1148.         [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
  1149.     return self;
  1150. }
  1151.  
  1152. - (int)linkKind
  1153. {
  1154.     return linkKind;
  1155. }
  1156.  
  1157. - setLinkKind:(int)aLinkKind
  1158. {
  1159.     linkKind = aLinkKind;
  1160.     if(child)
  1161.         [child setLinkKind:aLinkKind];
  1162.     if(sibling)
  1163.         [sibling setLinkKind:aLinkKind];
  1164.     if(!parent)
  1165.         [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
  1166.     return self;
  1167. }
  1168.  
  1169. - changeLinkKind
  1170. {
  1171.     linkKind = (linkKind + 1) % 2;
  1172.     if(child)
  1173.         [child changeLinkKind];
  1174.     if(sibling)
  1175.         [sibling changeLinkKind];
  1176.     if(!parent)
  1177.         [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
  1178.     return self;
  1179. }
  1180.  
  1181. - (float)biegFactor
  1182. {
  1183.     return biegFactor;
  1184. }
  1185.  
  1186. - setBiegFactor:(float)aBiegFactor
  1187. {
  1188.     biegFactor = aBiegFactor;
  1189.     if(child)
  1190.         [child setBiegFactor:aBiegFactor];
  1191.     if(sibling)
  1192.         [sibling setBiegFactor:aBiegFactor];
  1193.     if(!parent)
  1194.         [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
  1195.     return self;
  1196. }
  1197.  
  1198. //************************************************************************
  1199. // node properties
  1200.  
  1201. - (const char *)label
  1202. {
  1203.     return [textCell stringValue];
  1204. }
  1205.  
  1206. - setLabel:(const char *)aLabel
  1207. {    
  1208.     [textCell setStringValue:aLabel];
  1209.     [self changeText];
  1210.     [self setUpdateLayout:YES];
  1211.     [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1212.     return self;
  1213. }
  1214.  
  1215. - setRTFDescription:(const char *)aDescription
  1216. {
  1217.     [nodeDescription setRTF:aDescription];
  1218.     [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW];
  1219.     return self;
  1220. }
  1221.  
  1222. - setDescription:(const char *)aDescription
  1223. {    
  1224.     [nodeDescription setAscii:aDescription];
  1225.     [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW];
  1226.     return self;
  1227. }
  1228.  
  1229. - changeDescriptionFrom:aTextView
  1230. {
  1231.     [nodeDescription setRTFFromTextView:aTextView];
  1232.     [delegate updateViewsDirty:YES rect:NULL flag:UPDATE_NONE];
  1233.     return self;    
  1234. }
  1235.  
  1236. - putDescriptionIn:aTextView
  1237. {
  1238.     [aTextView replaceSelWithRichText:[nodeDescription stream]];
  1239.     [nodeDescription freeStream];
  1240.     return self;
  1241. }
  1242.  
  1243. - (int)pathKind
  1244. {
  1245.     return pathKind;
  1246. }
  1247.  
  1248. - setPathKind:(int)aPathKind
  1249. {
  1250.     [gpath resetFill];
  1251.     pathKind = aPathKind;
  1252.     [self makePath];
  1253.     [self setUpdateLayout:YES];
  1254.     [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1255.     return self;
  1256. }
  1257.  
  1258. - (BOOL)outline
  1259. {
  1260.     return outline;
  1261. }
  1262.  
  1263. - setOutline:(BOOL)aOutline
  1264. {
  1265.     NXRect r;
  1266.     
  1267.     outline = aOutline;
  1268.     [self getBounds:&r];
  1269.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  1270.     return self;
  1271. }
  1272.  
  1273. - font
  1274. {
  1275.     return [textCell font];
  1276. }
  1277.  
  1278. - changeFont:sender
  1279. {
  1280.     [textCell setFont:[sender convertFont:[textCell font]]];
  1281.     [self changeText];
  1282.     [self setUpdateLayout:YES];
  1283.     [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1284.     return self;
  1285. }
  1286.  
  1287. - (NXColor)fillColor
  1288. {
  1289.     return fillColor;
  1290. }
  1291.  
  1292. - setFillColor:(NXColor)aColor
  1293. {
  1294.     NXRect r;
  1295.     
  1296.     fillColor = aColor;
  1297.     [self getBounds:&r];
  1298.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  1299.     return self;
  1300. }
  1301.  
  1302. - (NXColor)outlineColor
  1303. {
  1304.     return outlineColor;
  1305. }
  1306.  
  1307. - setOutlineColor:(NXColor)aColor
  1308. {
  1309.     NXRect r;
  1310.     
  1311.     outlineColor = aColor;
  1312.     [self getBounds:&r];
  1313.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  1314.     return self;
  1315. }
  1316.  
  1317. - (NXColor)textColor
  1318. {
  1319.     return [textCell textColor];
  1320. }
  1321.  
  1322. - setTextColor:(NXColor)aColor
  1323. {
  1324.     NXRect r;
  1325.     
  1326.     [textCell setTextColor:aColor];
  1327.     [self getBounds:&r];
  1328.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  1329.     return self;
  1330. }
  1331.  
  1332. - (float)linewidth
  1333. {
  1334.     return linewidth;
  1335. }
  1336.  
  1337. - setLinewidth:(float)aLinewidth
  1338. {
  1339.     NXRect r;
  1340.     
  1341.     if(aLinewidth > linewidth){
  1342.         linewidth = aLinewidth;
  1343.         [self getBounds:&r];
  1344.     } else {
  1345.         [self getBounds:&r];
  1346.         linewidth = aLinewidth;
  1347.     }
  1348.     [delegate updateViewsDirty:YES rect:&r flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1349.     return self;
  1350. }
  1351.  
  1352. - (BOOL)zipped
  1353. {
  1354.     return zipped;
  1355. }
  1356.  
  1357. - changeZipped
  1358. {
  1359.     zipped = ! zipped;
  1360.     [self setUpdateLayout:YES];
  1361.     [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1362.     return self;
  1363. }
  1364.  
  1365. - (int)ending
  1366. {
  1367.     return ending;
  1368. }
  1369.  
  1370. - setEnding:(int)aEnding
  1371. {
  1372.     NXRect r,br;
  1373.     
  1374.     [self getBounds:&br];
  1375.     ending = aEnding;
  1376.     [self getBounds:&r];
  1377.     NXUnionRect(&br,&r);
  1378.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  1379.     return self;
  1380. }
  1381.  
  1382. - (int)parentEnding;
  1383. {
  1384.     return parentEnding;
  1385. }
  1386.  
  1387. - setParentEnding:(int)aEnding
  1388. {
  1389.     NXRect r,br;
  1390.     
  1391.     [self getBounds:&br];
  1392.     parentEnding = aEnding;
  1393.     [self getBounds:&r];
  1394.     NXUnionRect(&br,&r);
  1395.     [delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
  1396.     return self;
  1397. }
  1398.         
  1399. //************************************************************************
  1400. // misc
  1401.  
  1402. - (BOOL)selected
  1403. {
  1404.     return selected;
  1405. }
  1406.  
  1407. - setSelected:(BOOL)aBOOL
  1408. {
  1409.     selected = aBOOL;
  1410.     return self;
  1411. }
  1412.      
  1413. - setUpdateLayout:(BOOL)aBool
  1414. {
  1415.     Tree *dummy;
  1416.  
  1417.     updateLayout = aBool;
  1418.     if(aBool)
  1419.         if(parent){
  1420.             [parent setUpdateLayout:YES];
  1421.             dummy = [parent child];
  1422.             dummy->updateLayout = YES;
  1423.             while([dummy sibling]){
  1424.                 dummy = [dummy sibling];
  1425.                 dummy->updateLayout = YES;    
  1426.             }
  1427.         }
  1428.     return self;
  1429. }
  1430.  
  1431. - positionFrom:(const NXPoint *)aPoint 
  1432. {
  1433.     pos.x = aPoint->x + offset.x;
  1434.     pos.y = aPoint->y + offset.y;
  1435.     [self changePos];
  1436.     if(child)
  1437.         [child positionFrom:&pos];
  1438.     if(sibling)
  1439.         [sibling positionFrom:&pos];
  1440.     return self;
  1441. }
  1442.  
  1443. - setPositionTo:(const NXPoint *)aPoint
  1444. {
  1445.     pos.x = aPoint->x;
  1446.     pos.y = aPoint->y;
  1447.     [self changePos];
  1448.     if(child)
  1449.         [child positionFrom:&pos];
  1450.     if(sibling)
  1451.         [sibling positionFrom:&pos];
  1452.     return self;
  1453. }
  1454.  
  1455. - linkPoint:(NXPoint *)aPoint
  1456. {
  1457.     aPoint->x = pos.x + deltaLink.x;
  1458.     aPoint->y = pos.y + deltaLink.y;
  1459.     return self;
  1460. }
  1461.  
  1462. - linkParentPoint:(NXPoint *)aPoint
  1463. {
  1464.     aPoint->x = pos.x + width + (parentDistance + border) * (1 - biegFactor) / 2;
  1465.     aPoint->y = pos.y + deltaLink.y;
  1466.     return self;
  1467. }
  1468.  
  1469. - linkChildPoint:(NXPoint *)aPoint
  1470. {
  1471.     aPoint->x = pos.x - (parentDistance + border) * (1 - biegFactor) / 2;
  1472.     aPoint->y = pos.y + deltaLink.y;
  1473.     return self;
  1474. }
  1475.  
  1476. - delegate
  1477. {
  1478.     return delegate;
  1479. }
  1480.  
  1481. - setDelegate:aDelegate
  1482. {
  1483.     id oldDelegate;
  1484.     
  1485.     oldDelegate = delegate;
  1486.     delegate = aDelegate;
  1487.     return oldDelegate;
  1488. }
  1489.  
  1490. - setDelegateRecursive:aDelegate
  1491. {
  1492.     delegate = aDelegate;
  1493.     if(child)
  1494.         [child setDelegateRecursive:aDelegate];
  1495.     if(sibling)
  1496.         [sibling setDelegateRecursive:aDelegate];
  1497.     return self;
  1498. }
  1499.  
  1500. - textDidEnd:textObject endChar:(unsigned short)endChar
  1501. {
  1502.     int maxlen,length;
  1503.     char *buffer;
  1504.     NXStream *stream;
  1505.     
  1506.     [textObject removeFromSuperview];
  1507.     [textObject setDelegate:nil];
  1508.     [textObject setSel:0 :0];
  1509.     if([textObject textLength]){
  1510.         stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  1511.         [textObject writeText:stream];
  1512.         NXGetMemoryBuffer(stream, &buffer, &length, &maxlen);
  1513.         [textCell setStringValue:buffer];
  1514.         NXCloseMemory(stream, NX_FREEBUFFER);
  1515.     }
  1516.     [self changeText];
  1517.     [self setUpdateLayout:YES];
  1518.     [delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
  1519.     return self;
  1520. }
  1521.  
  1522. //*************************************************************************
  1523. // streaming to diagram2 format
  1524.  
  1525.  
  1526. - (unsigned)dgSymbolNr
  1527. {
  1528.     return dgSymbolNr;
  1529. }
  1530.  
  1531. - dgWriteSymbol:(NXStream *)stream buffer:(char *)buffer filename:(const char *)aName
  1532. {
  1533.     id font;
  1534.     char rtfItalic[3], rtfBold[3];
  1535.     unsigned i;
  1536.     int err;
  1537.  
  1538.     dgSymbolNr = [Tree dgNextNr];
  1539.     sprintf(buffer,"symbol %d\n", dgSymbolNr);
  1540.     NXWrite(stream,buffer,strlen(buffer));
  1541.     sprintf(buffer,"\tshape \"%s\"\n", dgShapeSymbolName[pathKind]);
  1542.     NXWrite(stream,buffer,strlen(buffer));
  1543.     sprintf(buffer,"\tlocation %.2f %.2f\n",pos.x,pos.y);
  1544.     NXWrite(stream,buffer,strlen(buffer));
  1545.     sprintf(buffer,"\tsize %.2f %.2f\n",width,height);
  1546.     NXWrite(stream,buffer,strlen(buffer));
  1547.     if(outline)
  1548.         NXWrite(stream,"\tframed\n", 8);
  1549.     if(fill)
  1550.         NXWrite(stream,"\tfilled\n", 8);
  1551.     if(shadow)
  1552.         NXWrite(stream,"\tshadowed\n", 10);
  1553.     if(outline){
  1554.         i = 0;
  1555.         while(i < [dgColorList colorCount] && 
  1556.             !NXEqualColor(outlineColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
  1557.             i++;
  1558.         if(i == [dgColorList colorCount]){
  1559.             sprintf(buffer,"woodColorIndexed%d",i);
  1560.             [dgColorList setColorNamed:buffer color:outlineColor];
  1561.         }
  1562.         sprintf(buffer,"\tframeColor colorIndex %d\n",i);
  1563.         NXWrite(stream,buffer,strlen(buffer));
  1564.     }
  1565.     if(fill){
  1566.         i = 0;
  1567.         while(i < [dgColorList colorCount] && 
  1568.             !NXEqualColor(fillColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
  1569.             i++;
  1570.         if(i == [dgColorList colorCount]){
  1571.             sprintf(buffer,"woodColorIndexed%d",i);
  1572.             [dgColorList setColorNamed:buffer color:fillColor];
  1573.         }
  1574.         sprintf(buffer,"\tfillColor colorIndex %d\n",i);
  1575.         NXWrite(stream,buffer,strlen(buffer));
  1576.     }
  1577.     if(shadow){
  1578.         i = 0;
  1579.         while(i < [dgColorList colorCount] && 
  1580.             !NXEqualColor(shadowColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
  1581.             i++;
  1582.         if(i == [dgColorList colorCount]){
  1583.             sprintf(buffer,"woodColorIndexed%d",i);
  1584.             [dgColorList setColorNamed:buffer color:shadowColor];
  1585.         }
  1586.         sprintf(buffer,"\tshadowColor colorIndex %d\n",i);
  1587.         NXWrite(stream,buffer,strlen(buffer));
  1588.     }
  1589.     sprintf(buffer,"\tlineWidth %.2f\n",linewidth);
  1590.     NXWrite(stream,buffer,strlen(buffer));
  1591.     if(hasVerknuepfung){
  1592.         sprintf(buffer,"\tattachment \"%s\"\n",[verknuepfung sourceFilename]);
  1593.         NXWrite(stream,buffer,strlen(buffer));
  1594.     }    
  1595.     if(hasSound){
  1596.         sprintf(buffer,"%s/Sound%d.snd",aName,dgSymbolNr);
  1597.         err = [geraeusch writeSoundfile:buffer];
  1598.         if(err)
  1599.             NXRunAlertPanel("Save Sound","Cannot write %s","OK",NULL,NULL,buffer);
  1600.         else {
  1601.             sprintf(buffer,"\tsoundFile \"Sound%d.snd\"\n",dgSymbolNr);
  1602.             NXWrite(stream,buffer,strlen(buffer));
  1603.         }
  1604.     }
  1605.     font = [textCell font];
  1606.     if(strstr([font displayName], "Bold"))
  1607.         strcpy(rtfBold,"b");
  1608.     else
  1609.         strcpy(rtfBold,"b0");
  1610.     if(strstr([font displayName], "Italic") || strstr([font displayName], "Oblique"))
  1611.         strcpy(rtfItalic,"i");
  1612.     else
  1613.         strcpy(rtfItalic,"i0");
  1614.     sprintf(buffer, dgRtfControlName, [font familyName], rtfBold, rtfItalic, [font pointSize] * 2, [textCell stringValue]);
  1615.     NXWrite(stream,buffer,strlen(buffer));
  1616.     NXWrite(stream,"\n",1);
  1617.     NXWrite(stream,"\ttextPlacement middle\n", 22);
  1618.     NXWrite(stream,dgEndName, strlen(dgEndName));
  1619.     if(child)
  1620.         [child dgWriteSymbol:stream buffer:buffer filename:aName];
  1621.     if(sibling)
  1622.         [sibling dgWriteSymbol:stream buffer:buffer filename:aName];
  1623.     return self;
  1624. }
  1625.  
  1626. - dgWriteVertex:(NXStream *)stream buffer:(char *)buffer
  1627. {
  1628.     NXPoint p2,p3;
  1629.  
  1630.     if(parent){
  1631.         dgVertexNr[0] = [Tree dgNextNr];
  1632.         sprintf(buffer,"vertex %d\n",dgVertexNr[0]);
  1633.         NXWrite(stream,buffer,strlen(buffer)); 
  1634.         NXWrite(stream,dgEndName, strlen(dgEndName));
  1635.         dgVertexNr[1] = [Tree dgNextNr];
  1636.         sprintf(buffer,"vertex %d\n",dgVertexNr[1]);
  1637.         NXWrite(stream,buffer,strlen(buffer)); 
  1638.         NXWrite(stream,dgEndName, strlen(dgEndName));
  1639.         if(linkKind){
  1640.             [parent linkParentPoint:&p3];
  1641.             [self linkChildPoint:&p2];
  1642.             dgVertexNr[2] = [Tree dgNextNr];
  1643.             sprintf(buffer,"vertex %d\n",dgVertexNr[2]);
  1644.             NXWrite(stream,buffer,strlen(buffer)); 
  1645.             sprintf(buffer,"\tlocation %.2f %.2f\n", p2.x, p2.y);
  1646.             NXWrite(stream,buffer,strlen(buffer));
  1647.             NXWrite(stream,dgEndName, strlen(dgEndName));
  1648.             dgVertexNr[3] = [Tree dgNextNr];
  1649.             sprintf(buffer,"vertex %d\n",dgVertexNr[3]);
  1650.             NXWrite(stream,buffer,strlen(buffer)); 
  1651.             sprintf(buffer,"\tlocation %.2f %.2f\n", p3.x, p3.y);
  1652.             NXWrite(stream,buffer,strlen(buffer));
  1653.             NXWrite(stream,dgEndName, strlen(dgEndName));
  1654.         }
  1655.     }
  1656.     if(child)
  1657.         [child dgWriteVertex:stream buffer:buffer];
  1658.     if(sibling)
  1659.         [sibling dgWriteVertex:stream buffer:buffer];
  1660.     return self;
  1661. }
  1662.  
  1663. - dgWriteLine:(NXStream *)stream buffer:(char *)buffer
  1664. {
  1665.     if(parent){
  1666.         sprintf(buffer,"line %d\n",[Tree dgNextNr]);
  1667.         NXWrite(stream,buffer,strlen(buffer));
  1668.         sprintf(buffer,"\tfrom %d\n",dgSymbolNr);
  1669.         NXWrite(stream,buffer,strlen(buffer));
  1670.         sprintf(buffer,"\tto %d\n",[parent dgSymbolNr]);
  1671.         NXWrite(stream,buffer,strlen(buffer));
  1672.         if(ending != ENDING_NONE){
  1673.             switch(ending){
  1674.                 case ENDING_ARROW:
  1675.                     sprintf(buffer,"\ttailType arrow\n");
  1676.                 break;
  1677.                 case ENDING_HOLLOW:
  1678.                     sprintf(buffer,"\ttailType hollowArrow\n");
  1679.                 break;
  1680.                 case ENDING_DOUBLE:
  1681.                     sprintf(buffer,"\ttailType doubleArrow\n");
  1682.                 break;
  1683.                 case ENDING_SOLID:
  1684.                     sprintf(buffer,"\ttailType solidCircle\n");
  1685.                 break;
  1686.                 case ENDING_CIRCLE:
  1687.                     sprintf(buffer,"\ttailType circle\n");
  1688.                 break;
  1689.                 default:
  1690.                 break;
  1691.             }
  1692.             NXWrite(stream,buffer,strlen(buffer));
  1693.         }
  1694.         if([parent parentEnding] != ENDING_NONE){
  1695.             switch([parent parentEnding]){
  1696.                 case ENDING_ARROW:
  1697.                     sprintf(buffer,"\theadType arrow\n");
  1698.                 break;
  1699.                 case ENDING_HOLLOW:
  1700.                     sprintf(buffer,"\theadType hollowArrow\n");
  1701.                 break;
  1702.                 case ENDING_DOUBLE:
  1703.                     sprintf(buffer,"\theadType doubleArrow\n");
  1704.                 break;
  1705.                 case ENDING_SOLID:
  1706.                     sprintf(buffer,"\theadType solidCircle\n");
  1707.                 break;
  1708.                 case ENDING_CIRCLE:
  1709.                     sprintf(buffer,"\theadType circle\n");
  1710.                 break;
  1711.                 default:
  1712.                 break;
  1713.             }
  1714.             NXWrite(stream,buffer,strlen(buffer));
  1715.         }
  1716.         sprintf(buffer,"\t%d\n",dgVertexNr[0]);
  1717.         NXWrite(stream,buffer,strlen(buffer));
  1718.         if(linkKind){
  1719.             sprintf(buffer,"\t%d\n",dgVertexNr[2]);
  1720.             NXWrite(stream,buffer,strlen(buffer));
  1721.             sprintf(buffer,"\t%d\n",dgVertexNr[3]);
  1722.             NXWrite(stream,buffer,strlen(buffer));
  1723.         }    
  1724.         sprintf(buffer,"\t%d\n",dgVertexNr[1]);
  1725.         NXWrite(stream,buffer,strlen(buffer));
  1726.         NXWrite(stream,dgEndName, strlen(dgEndName));
  1727.     }
  1728.     if(child)
  1729.         [child dgWriteLine:stream buffer:buffer];
  1730.     if(sibling)
  1731.         [sibling dgWriteLine:stream buffer:buffer];
  1732.     return self;
  1733. }
  1734.  
  1735. + saveToD2Tree:aTree fromView:aView toFile:(const char *)aFile printInfo:aPrintInfo
  1736. {
  1737.     NXStream *stream;
  1738.     NXTypedStream *typedStream;
  1739.     char fileNameBuffer[MAXPATHLEN],commandBuffer[MAXPATHLEN + 20];
  1740.     char buffer[1024];
  1741.     NXRect dummyRect; 
  1742.     
  1743.     dgColorList = [[NXColorList alloc] init];
  1744.     [aView getBounds:&dummyRect];
  1745.     dgNextNr = 0;
  1746.     stream = NXOpenMemory(NULL,0,NX_WRITEONLY);
  1747.     NXWrite(stream, dgDocuBegin,strlen(dgDocuBegin));
  1748.     strcpy(fileNameBuffer,aFile);
  1749.     sprintf(commandBuffer,"mkdir %s",fileNameBuffer);
  1750.     system(commandBuffer);
  1751.     if(aTree){
  1752.         NXWrite(stream, dgSymbolComment,strlen(dgSymbolComment));
  1753.         [aTree dgWriteSymbol:stream buffer:buffer filename:aFile];
  1754.         NXWrite(stream, dgVertexComment,strlen(dgVertexComment));
  1755.         [aTree dgWriteVertex:stream buffer:buffer];
  1756.         NXWrite(stream, dgLineComment,strlen(dgLineComment));
  1757.         [aTree dgWriteLine:stream buffer:buffer];
  1758.     }
  1759.     strcpy(fileNameBuffer,aFile);
  1760.     strcat(fileNameBuffer,"/DiagramText");
  1761.     NXSaveToFile(stream, fileNameBuffer);              
  1762.     NXCloseMemory(stream,NX_FREEBUFFER);
  1763.     strcpy(fileNameBuffer,aFile);
  1764.     strcat(fileNameBuffer,"/PrintInfo");
  1765.     stream = NXOpenMemory(NULL,0,NX_WRITEONLY);
  1766.     typedStream = NXOpenTypedStream(stream,NX_WRITEONLY);
  1767.     NXWriteRootObject(typedStream,aPrintInfo);
  1768.     NXCloseTypedStream(typedStream);
  1769.     NXSeek(stream, - 1, NX_FROMCURRENT);
  1770.     NXWrite(stream,dgPrintInfoEnd,6);
  1771.     NXSaveToFile(stream, fileNameBuffer);
  1772.     NXCloseMemory(stream,NX_FREEBUFFER);
  1773.     strcpy(fileNameBuffer,aFile);
  1774.     strcat(fileNameBuffer,"/Colors.clr");
  1775.     [dgColorList saveTo:fileNameBuffer];
  1776.     [dgColorList free];
  1777.     dgColorList = nil;
  1778.     return self;
  1779. }
  1780.  
  1781. + (unsigned)dgNextNr
  1782. {
  1783.     dgNextNr++;
  1784.     return dgNextNr;
  1785. }
  1786.  
  1787. //*************************************************************************
  1788. // streaming
  1789.  
  1790. - writeStyleToPBStream:(NXTypedStream *)stream
  1791. {
  1792.     NXWriteTypes(stream,"iiiif",&outline,&pathKind,&ending,&parentEnding,&linewidth);
  1793.     NXWriteColor(stream,fillColor);
  1794.     NXWriteColor(stream,outlineColor);
  1795.     NXWriteColor(stream,[textCell textColor]);
  1796.     NXWriteObject(stream,[textCell font]);
  1797.     return self;
  1798. }
  1799.  
  1800. - readStyleFromPBStream:(NXTypedStream *)stream
  1801. {
  1802.     Properties props;
  1803.     
  1804.     NXReadTypes(stream,"iiiif",&(props.outline),&(props.pathKind),&(props.ending),&(props.parentEnding),&(props.linewidth));
  1805.     props.fillColor = NXReadColor(stream);
  1806.     props.outlineColor = NXReadColor(stream);
  1807.     props.textColor = NXReadColor(stream);
  1808.     props.font = NXReadObject(stream);
  1809.     [self changeTo:&props];
  1810.     return self;
  1811. }
  1812.  
  1813. - writeToPBStream:(NXTypedStream *)stream
  1814. {
  1815.     Tree *tmpParent, *tmpSibling;
  1816.     
  1817.     tmpParent = parent;
  1818.     tmpSibling = sibling;
  1819.     parent = sibling = nil;
  1820.     NXWriteRootObject(stream, self);
  1821.     parent = tmpParent;
  1822.     sibling = tmpSibling;
  1823.     return self;
  1824. }
  1825.  
  1826. - writeToMMAPB:(NXStream *)stream
  1827. {
  1828.     if(child){
  1829.         NXWrite(stream,"{",1);
  1830.         NXWrite(stream,[textCell stringValue],strlen([textCell stringValue]));
  1831.         NXWrite(stream,", ",2);
  1832.         [child writeToMMAPB:stream];
  1833.         NXWrite(stream,"}",1);
  1834.     } else {
  1835.         NXWrite(stream,[textCell stringValue],strlen([textCell stringValue]));
  1836.     }
  1837.     if(sibling){
  1838.         NXWrite(stream,", ",2);
  1839.         [sibling writeToMMAPB:stream];
  1840.     }
  1841.     return self;
  1842. }
  1843.  
  1844. - write:(NXTypedStream *)stream
  1845. {
  1846.     //BOOL hasChild,hasSibling;
  1847.     int hasChild,hasSibling;
  1848.     id retval;
  1849.  
  1850.     NX_DURING
  1851.            [super write:stream];
  1852.         hasChild = (child ? YES : NO);
  1853.         hasSibling = (sibling ? YES :NO);
  1854.         NXWriteTypes(stream,"ii",&hasVerknuepfung,&hasSound);
  1855.         NXWriteTypes(stream,"iiff",&hasChild,&hasSibling,&parentDistance,&border);
  1856.         NXWriteTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor);
  1857.         NXWriteTypes(stream,"ii",&ending,&parentEnding);
  1858.         NXWriteColor(stream,fillColor);
  1859.         NXWriteColor(stream,outlineColor);
  1860.         NXWriteColor(stream,shadowColor);
  1861.         NXWriteObject(stream,textCell);
  1862.         NXWriteObject(stream,nodeDescription);
  1863.         NXWriteObjectReference(stream, parent);
  1864.         if(hasChild)
  1865.             NXWriteObject(stream, child);
  1866.         if(hasSibling)
  1867.             NXWriteObject(stream, sibling);
  1868.         if(hasVerknuepfung)
  1869.             NXWriteObject(stream,verknuepfung);
  1870.         if(hasSound)
  1871.             NXWriteObject(stream,geraeusch);
  1872.            retval = self;
  1873.     NX_HANDLER
  1874.         retval = nil;
  1875.     NX_ENDHANDLER
  1876.     return retval;
  1877. }
  1878.  
  1879. - read:(NXTypedStream *)stream
  1880. {
  1881.     //BOOL hasChild,hasSibling;
  1882.     int hasChild,hasSibling;
  1883.     id retval;
  1884.  
  1885.     NX_DURING
  1886.         NXSetTypedStreamZone(stream, [self zone]);
  1887.            [super read:stream];
  1888.         NXReadTypes(stream,"ii",&hasVerknuepfung,&hasSound);
  1889.         NXReadTypes(stream,"iiff", &hasChild,&hasSibling,&parentDistance,&border);
  1890.         if(NXTypedStreamClassVersion(stream, "Tree") == [Tree version]) 
  1891.             NXReadTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor);
  1892.         else {
  1893.             NXReadTypes(stream,"iiiiif",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth);
  1894.             biegFactor = 0.33;
  1895.         }    
  1896.         NXReadTypes(stream,"ii",&ending,&parentEnding);
  1897.         fillColor = NXReadColor(stream);
  1898.         outlineColor = NXReadColor(stream);
  1899.         shadowColor = NXReadColor(stream);
  1900.         textCell = NXReadObject(stream);
  1901.         nodeDescription = NXReadObject(stream);
  1902.         parent = NXReadObject(stream);
  1903.         if(hasChild)
  1904.             child = NXReadObject(stream);
  1905.         else
  1906.             child = nil;
  1907.         if(hasSibling)
  1908.             sibling = NXReadObject(stream);
  1909.         else
  1910.             sibling = nil;
  1911.         if(hasVerknuepfung)
  1912.             verknuepfung = NXReadObject(stream);
  1913.         if(hasSound)
  1914.             geraeusch = NXReadObject(stream);
  1915.         retval = self;
  1916.     NX_HANDLER
  1917.         retval = nil;
  1918.     NX_ENDHANDLER
  1919.        return retval;
  1920. }
  1921.  
  1922. - awake
  1923. {
  1924.        [super awake];
  1925.     contour.lower.head = contour.upper.head = NULL;
  1926.        contour.lower.tail = contour.upper.tail = NULL;
  1927.     offset.x = offset.y = 0.0;
  1928.     pos.x = pos.y = 0.0; 
  1929.     gpath = [[UPath allocFromZone:[self zone]] init];
  1930.     deltaShadow.x = deltaShadow.y = 3;
  1931.     placer.rect[0] = placer.rect[1] = 6;
  1932.     placer.roundRect[0] = placer.roundRect[1] = 6;
  1933.     placer.roundRect[2] = 3;
  1934.     placer.ellipse[0] = 6;
  1935.     placer.ellipse[1] = 4;
  1936.     placer.rhomb[0] = 12;
  1937.     placer.rhomb[1] = 3;
  1938.     placer.hexagon[0] = 6;
  1939.     placer.hexagon[1] = 6;
  1940.     placer.hexagon[2] = 1;
  1941.     [self makePath]; 
  1942.     selected = NO;    
  1943.     updateLayout = YES;
  1944.     delegate = nil;
  1945.     fill = YES;
  1946.        return self;
  1947. }
  1948.  
  1949. //*************************************************************************
  1950. // SearchableText support
  1951.  
  1952. - searchTreeFor:(const char *)pattern untilNode:endNode 
  1953.   reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(int *)positionS size:(int *)sizeS
  1954. {
  1955.     MiscString *labelS;
  1956.     id result;
  1957.     int resultPos;
  1958.     
  1959.     result = nil;
  1960.     resultPos = -1;
  1961.     labelS = [[MiscString alloc] initString:[self label]];
  1962.     if(regexpr)
  1963.         resultPos = [labelS spotOfRegex:pattern caseSensitive:cases length:sizeS];
  1964.     else
  1965.         resultPos = [labelS spotOfStr:pattern caseSensitive:cases];
  1966.     if(resultPos > -1){ 
  1967.         *positionS = resultPos;
  1968.         if(!regexpr)
  1969.             *sizeS = strlen(pattern);
  1970.         result = self;
  1971.     } else if(self == endNode)
  1972.         return nil;
  1973.     else {
  1974.         if(rev)
  1975.             result = [[self previousInDepth] searchTreeFor:pattern untilNode:endNode 
  1976.                          reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS];
  1977.         else
  1978.             result = [[self nextInDepth] searchTreeFor:pattern untilNode:endNode 
  1979.                          reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS];
  1980.     }
  1981.     [labelS free];
  1982.     return result;
  1983. }
  1984.  
  1985. - replaceTreeFor:(const char *)pattern with:(const char *)replacement untilNode:endNode 
  1986.   regexpr:(BOOL)regexpr cases:(BOOL)cases result:(int *)result
  1987. {
  1988.     MiscString *labelS;
  1989.     int nr;
  1990.     
  1991.     nr = 0;
  1992.     labelS = [[MiscString alloc] initString:[self label]];
  1993.     if(regexpr)
  1994.         nr = [labelS replaceEveryOccurrenceOfRegex:pattern with:replacement caseSensitive:cases];
  1995.     else
  1996.         nr = [labelS replaceEveryOccurrenceOf:pattern with:replacement caseSensitive:cases];
  1997.     if(nr > 0){
  1998.         [textCell setStringValue:[labelS stringValue]];
  1999.         [self changeText];
  2000.         [self setUpdateLayout:YES];
  2001.     }
  2002.     *result += nr;
  2003.     if(self != endNode)
  2004.         [[self nextInDepth] replaceTreeFor:pattern with:replacement untilNode:endNode 
  2005.             regexpr:regexpr cases:cases result:result];
  2006.     [labelS free];
  2007.     return self;
  2008. }
  2009.     
  2010. - nextInDepth
  2011. {
  2012.     Tree *r, *t;
  2013.     
  2014.     if(child)
  2015.         r = child;
  2016.     else if(sibling)
  2017.         r = sibling;
  2018.     else {
  2019.         t = self;
  2020.         while([t parent] && ![t sibling])
  2021.             t = [t parent];
  2022.         if([t sibling])    
  2023.             r = [t sibling];
  2024.         else
  2025.             r = t;
  2026.     }
  2027.     return r;
  2028. }
  2029.  
  2030. - previousInDepth
  2031. {
  2032.     Tree *r, *t;
  2033.     
  2034.     if(!parent)
  2035.          return [self lastInDepth];
  2036.     if([parent child] == self)
  2037.         return parent;
  2038.     else {
  2039.         r = [parent child];
  2040.         t = [r sibling];
  2041.         while(t != self){
  2042.             r = t;
  2043.             t = [t sibling];
  2044.         }
  2045.         return [r lastInDepth];
  2046.     }
  2047. }            
  2048.  
  2049. - lastInDepth
  2050. {
  2051.     Tree *r, *t;
  2052.     
  2053.     r = self;
  2054.     t = child;
  2055.     while(t){
  2056.         r = t;
  2057.         while(t){
  2058.             r = t;
  2059.             t = [t sibling];
  2060.         }
  2061.         t = [r child];
  2062.     }
  2063.     return r;
  2064. }
  2065.  
  2066. - (BOOL)replaceLabelSelectionWith:(const char *)replacement pos:(int)aPos size:(int)aSize
  2067. {
  2068.     MiscString *labelS, *pattern;
  2069.     int nr;
  2070.     BOOL result = NO;
  2071.     
  2072.     labelS = [[MiscString alloc] initString:[self label]];
  2073.     if(aPos + aSize > [labelS length])
  2074.         return result;
  2075.     pattern = [labelS midFrom:aPos to:(aPos + aSize - 1) fromZone:[self zone]];
  2076.     nr = [labelS replaceEveryOccurrenceOfString:pattern with:replacement caseSensitive:YES];
  2077.     if(nr > 0){
  2078.         [textCell setStringValue:[labelS stringValue]];
  2079.         [self changeText];
  2080.         [self setUpdateLayout:YES];
  2081.         result = YES;
  2082.     }
  2083.     [labelS free];
  2084.     return result;
  2085. }
  2086.                        
  2087. @end
  2088.  
  2089. @implementation Tree(PrivateMethods)
  2090.  
  2091. - attachParent:(NXCoord)h
  2092. {
  2093.     NXCoord x,y1,y2;
  2094.  
  2095.     x = border + parentDistance;
  2096.     y2 = (h - height) / 2 - border;
  2097.     y1 = y2 + height + 2 * border - h;
  2098.     [child setOffset:x + width :y1];  
  2099.     contour.upper.head = allocLine(width,0,allocLine(x,y1,contour.upper.head,[self zone]),[self zone]);
  2100.     contour.lower.head = allocLine(width,0,allocLine(x,y2,contour.lower.head,[self zone]),[self zone]);
  2101.     return self;
  2102. }
  2103.  
  2104. - layoutLeaf
  2105. {
  2106.     if(contour.lower.head)
  2107.         freePolyline(contour.lower.head);
  2108.     if(contour.upper.head)
  2109.         freePolyline(contour.upper.head);
  2110.     contour.upper.tail = allocLine(width + 2 * border,0,NULL,[self zone]);
  2111.     contour.upper.head = contour.upper.tail;
  2112.     contour.lower.tail = allocLine(0, - height - 2 * border,NULL,[self zone]);
  2113.     contour.lower.head = allocLine(width + 2 * border,0,contour.lower.tail,[self zone]);
  2114.     return self;
  2115. }
  2116.  
  2117. - (NXCoord)join
  2118. {
  2119.     Tree *c;
  2120.     NXCoord d,h,sum;
  2121.     Polygon dummy;
  2122.  
  2123.     c = child;
  2124.     if(contour.lower.head)
  2125.         freePolyline(contour.lower.head);
  2126.     if(contour.upper.head)
  2127.         freePolyline(contour.upper.head);
  2128.     if(c->contour.upper.head)
  2129.            contour.upper.head = copyPolyline(c->contour.upper.head,&(contour.upper.tail),[self zone]);
  2130.     else
  2131.         contour.upper.head = contour.upper.tail = NULL;
  2132.     if(c->contour.lower.head)
  2133.            contour.lower.head = copyPolyline(c->contour.lower.head,&(contour.lower.tail),[self zone]);
  2134.     else
  2135.         contour.lower.head = contour.lower.tail = NULL;
  2136.     sum = h = [c height] + 2 * [c border];
  2137.     c = [c sibling];
  2138.     while(c) {
  2139.         if(c->contour.upper.head)
  2140.                dummy.upper.head = copyPolyline(c->contour.upper.head,&(dummy.upper.tail),[self zone]);
  2141.         else
  2142.             dummy.upper.head = dummy.upper.tail = NULL;
  2143.         if(c->contour.lower.head)
  2144.                dummy.lower.head = copyPolyline(c->contour.lower.head,&(dummy.lower.tail),[self zone]);
  2145.         else
  2146.             dummy.lower.head = dummy.lower.tail = NULL;
  2147.         d = merge(&contour,&dummy,[self zone]);
  2148.         [c setOffset:0 :d + h];
  2149.         h = [c height] + 2 * [c border];
  2150.         sum += d + h;
  2151.         c = [c sibling];
  2152.     } 
  2153.     return sum;
  2154. }
  2155.  
  2156. - makePath
  2157. {
  2158.     float r;
  2159.     
  2160.     [self calcWidthHeight];
  2161.     switch(pathKind){
  2162.         case RECT_NODETYPE:
  2163.             [gpath moveto:pos.x :pos.y];
  2164.             [gpath rlineto:width :0.0];
  2165.             [gpath rlineto:0.0 :height];
  2166.             [gpath rlineto:-width :0.0];
  2167.             [gpath rlineto:0.0 :-height];
  2168.             break;
  2169.         case ROUNDRECT_NODETYPE:
  2170.             r = MIN(width / 3, height / 3);
  2171.             [gpath moveto:pos.x :pos.y + r];
  2172.             [gpath lineto:pos.x :pos.y + height - r];
  2173.             [gpath curveto:pos.x :pos.y + height - r + r * 0.5519
  2174.                     :pos.x + r - r * 0.5519 :pos.y + height
  2175.                     :pos.x + r :pos.y + height];
  2176.             [gpath lineto:pos.x + width - r :pos.y + height];
  2177.             [gpath curveto:pos.x + width - r + r * 0.5519 :pos.y + height
  2178.                     :pos.x + width :pos.y + height - r + r * 0.5519
  2179.                     :pos.x + width :pos.y + height - r];
  2180.             [gpath lineto:pos.x + width :pos.y + r];
  2181.             [gpath curveto:pos.x + width :pos.y + r - r * 0.5519
  2182.                     :pos.x + width - r + r * 0.5519 :pos.y
  2183.                     :pos.x + width - r :pos.y];
  2184.             [gpath lineto:pos.x + r :pos.y];
  2185.             [gpath curveto:pos.x + r - r * 0.5519 :pos.y
  2186.                     :pos.x :pos.y + r - r * 0.5519  
  2187.                     :pos.x :pos.y + r];
  2188.         /*    [gpath moveto:(pos.x + height / 3)  :pos.y];
  2189.             [gpath arct:(pos.x + width) :pos.y :(pos.x + width) :(pos.y + height) :height / 3];
  2190.             [gpath arct:(pos.x + width) :(pos.y + height) :pos.x :(pos.y + height) :height / 3];
  2191.             [gpath arct:pos.x :(pos.y + height) :pos.x :pos.y : height / 3];
  2192.             [gpath arct:pos.x :pos.y :(pos.x + width) :pos.y :height / 3];  */
  2193.             break;
  2194.         case ELLIPSE_NODETYPE:
  2195.             [gpath moveto:(pos.x + width) :(pos.y + height / 2)];
  2196.             [gpath curveto:(pos.x + width) :(pos.y + height / 2 * 0.445)
  2197.                         :(pos.x + width / 2 * 1.555)  :pos.y
  2198.                         :(pos.x + width / 2) :pos.y];
  2199.             [gpath curveto:(pos.x + width / 2 * 0.445) :pos.y
  2200.                         :pos.x :(pos.y + height / 2 * 0.445)
  2201.                         :pos.x :(pos.y + height / 2)];
  2202.             [gpath curveto:pos.x :(pos.y + height /2 * 1.555)
  2203.                         :(pos.x + width / 2 * 0.445) :(pos.y + height)
  2204.                         :(pos.x + width / 2) :(pos.y + height)];
  2205.             [gpath curveto:(pos.x + width / 2 * 1.555) :(pos.y + height)
  2206.                         :(pos.x + width) :(pos.y + height / 2 * 1.555)
  2207.                         :(pos.x + width) :(pos.y + height / 2)];
  2208.             break;
  2209.         case RHOMB_NODETYPE: 
  2210.             [gpath moveto:pos.x + width / 2 :pos.y];
  2211.             [gpath rlineto:width / 2 :height / 2];
  2212.             [gpath rlineto:-width / 2 :height / 2];
  2213.             [gpath rlineto:-width / 2 :-height / 2];
  2214.             [gpath rlineto:width / 2 :-height / 2];
  2215.             break;
  2216.         case HEXAGON_NODETYPE:
  2217.             [gpath moveto:pos.x + placer.hexagon[0] :pos.y];
  2218.             [gpath rlineto:(width - 2 * placer.hexagon[0]) :0];
  2219.             [gpath rlineto:placer.hexagon[0] :height /2];
  2220.             [gpath rlineto:-placer.hexagon[0] :height /2];
  2221.             [gpath rlineto:(- width + 2 * placer.hexagon[0]) :0];
  2222.             [gpath rlineto:-placer.hexagon[0] :-height / 2];
  2223.             [gpath rlineto:placer.hexagon[0] :-height / 2];
  2224.             break;
  2225.         default:
  2226.             break;    
  2227.     } 
  2228.     return self;
  2229. }
  2230.  
  2231. - changeText
  2232. {
  2233.     [self calcWidthHeight];
  2234.     [self fillGPathParams];
  2235.     gpath->bbox[2] = gpath->bbox[0] + width;
  2236.     gpath->bbox[3] = gpath->bbox[1] + height; 
  2237.     return self;
  2238. }
  2239.  
  2240. - changePos
  2241. {
  2242.     NXPoint delta;
  2243.  
  2244.     [self fillGPathParams];
  2245.     switch(pathKind){
  2246.         case RECT_NODETYPE:
  2247.             delta.x = gpath->params[0] - gpath->bbox[0];
  2248.             delta.y = gpath->params[1] - gpath->bbox[1];
  2249.             break;
  2250.         case ROUNDRECT_NODETYPE:
  2251.             delta.x = gpath->params[0] - gpath->bbox[0];
  2252.             delta.y = gpath->params[23] - gpath->bbox[1];
  2253.             break;
  2254.         case ELLIPSE_NODETYPE:
  2255.             delta.x = gpath->params[10] - gpath->bbox[0];
  2256.             delta.y = gpath->params[5] - gpath->bbox[1];
  2257.             break;
  2258.         case RHOMB_NODETYPE:
  2259.             delta.x = gpath->params[0] - gpath->bbox[0] - width / 2;
  2260.             delta.y = gpath->params[1] - gpath->bbox[1];
  2261.             break;
  2262.         case HEXAGON_NODETYPE:
  2263.             delta.x = gpath->params[0] - gpath->bbox[0] - placer.hexagon[0];
  2264.             delta.y = gpath->params[1] - gpath->bbox[1];
  2265.             break;
  2266.         default:
  2267.             break;    
  2268.     }
  2269.     gpath->bbox[0] += delta.x;
  2270.        gpath->bbox[1] += delta.y;
  2271.     gpath->bbox[2] += delta.x;
  2272.     gpath->bbox[3] += delta.y;
  2273.     return self;
  2274. }
  2275.  
  2276. - fillGPathParams
  2277. {
  2278.     float r;
  2279.     
  2280.     switch(pathKind){
  2281.         case RECT_NODETYPE:
  2282.             gpath->params[0] = pos.x;
  2283.             gpath->params[1] = pos.y;
  2284.             gpath->params[2] = width;
  2285.             gpath->params[3] = 0.0;
  2286.             gpath->params[4] = 0.0;
  2287.             gpath->params[5] = height;
  2288.             gpath->params[6] = -width;
  2289.             gpath->params[7] = 0.0;
  2290.             gpath->params[8] = 0.0;
  2291.             gpath->params[9] = -height;
  2292.             break;
  2293.         case ROUNDRECT_NODETYPE:
  2294.             r = MIN(width / 3, height / 3);
  2295.             gpath->params[0] = pos.x;
  2296.             gpath->params[1] = pos.y + r;
  2297.             gpath->params[2] = pos.x;
  2298.             gpath->params[3] = pos.y + height - r;
  2299.             gpath->params[4] = pos.x;
  2300.             gpath->params[5] = pos.y + height - r + r * 0.5519;
  2301.             gpath->params[6] = pos.x + r - r * 0.5519;
  2302.             gpath->params[7] = pos.y + height;
  2303.             gpath->params[8] = pos.x + r;
  2304.             gpath->params[9] = pos.y + height;
  2305.             gpath->params[10] = pos.x + width - r;
  2306.             gpath->params[11] = pos.y + height;
  2307.             gpath->params[12] = pos.x + width - r + r * 0.5519;
  2308.             gpath->params[13] = pos.y + height;
  2309.             gpath->params[14] = pos.x + width;
  2310.             gpath->params[15] = pos.y + height - r + r * 0.5519;
  2311.             gpath->params[16] = pos.x + width;
  2312.             gpath->params[17] = pos.y + height - r;
  2313.             gpath->params[18] = pos.x + width;
  2314.             gpath->params[19] = pos.y + r;
  2315.             gpath->params[20] = pos.x + width;
  2316.             gpath->params[21] = pos.y + r - r * 0.5519;
  2317.             gpath->params[22] = pos.x + width - r + r * 0.5519;
  2318.             gpath->params[23] = pos.y;
  2319.             gpath->params[24] = pos.x + width - r;
  2320.             gpath->params[25] = pos.y;
  2321.             gpath->params[26] = pos.x + r;
  2322.             gpath->params[27] = pos.y;
  2323.             gpath->params[28] = pos.x + r - r * 0.5519;
  2324.             gpath->params[29] = pos.y;
  2325.             gpath->params[30] = pos.x;
  2326.             gpath->params[31] = pos.y + r - r * 0.5519;
  2327.             gpath->params[32] = pos.x;
  2328.             gpath->params[33] = pos.y + r;
  2329.         /*    gpath->params[0] = pos.x + height / 3;
  2330.             gpath->params[1] = pos.y;
  2331.             gpath->params[2] = pos.x + width;
  2332.             gpath->params[3] = pos.y;
  2333.             gpath->params[4] = pos.x + width;
  2334.             gpath->params[5] = pos.y + height;
  2335.             gpath->params[6] = height / 3;
  2336.             gpath->params[7] = pos.x + width;
  2337.             gpath->params[8] = pos.y + height;
  2338.             gpath->params[9] = pos.x;
  2339.             gpath->params[10] = pos.y + height;
  2340.             gpath->params[11] = height / 3;
  2341.             gpath->params[12] = pos.x;
  2342.             gpath->params[13] = pos.y + height;
  2343.             gpath->params[14] = pos.x;
  2344.             gpath->params[15] = pos.y;
  2345.             gpath->params[16] = height / 3;
  2346.             gpath->params[17] = pos.x;
  2347.             gpath->params[18] = pos.y;
  2348.             gpath->params[19] = pos.x + width;
  2349.             gpath->params[20] = pos.y;
  2350.             gpath->params[21] = height / 3; */
  2351.             break;
  2352.         case ELLIPSE_NODETYPE:
  2353.             gpath->params[0] = pos.x + width;
  2354.             gpath->params[1] = pos.y + height / 2;
  2355.             gpath->params[2] = pos.x + width;
  2356.             gpath->params[3] = pos.y + height / 2 * 0.445;
  2357.             gpath->params[4] = pos.x + width / 2 * 1.555;
  2358.             gpath->params[5] = pos.y;
  2359.             gpath->params[6] = pos.x + width / 2; 
  2360.             gpath->params[7] = pos.y;
  2361.             gpath->params[8] = pos.x + width / 2 * 0.445;
  2362.             gpath->params[9] = pos.y;
  2363.             gpath->params[10] = pos.x;
  2364.             gpath->params[11] = pos.y + height / 2 * 0.445;
  2365.             gpath->params[12] = pos.x;
  2366.             gpath->params[13] = pos.y + height / 2;
  2367.             gpath->params[14] = pos.x;
  2368.             gpath->params[15] = pos.y + height /2 * 1.555;
  2369.             gpath->params[16] = pos.x + width / 2 * 0.445;
  2370.             gpath->params[17] = pos.y + height;
  2371.             gpath->params[18] = pos.x + width / 2;
  2372.             gpath->params[19] = pos.y + height;
  2373.             gpath->params[20] = pos.x + width / 2 * 1.555;
  2374.             gpath->params[21] = pos.y + height;
  2375.             gpath->params[22] = pos.x + width;
  2376.             gpath->params[23] = pos.y + height / 2 * 1.555;
  2377.             gpath->params[24] = pos.x + width;
  2378.             gpath->params[25] = pos.y + height / 2;
  2379.             break;
  2380.         case RHOMB_NODETYPE:
  2381.             gpath->params[0] = pos.x + width / 2;
  2382.             gpath->params[1] = pos.y;
  2383.             gpath->params[2] = width / 2;
  2384.             gpath->params[3] = height / 2;
  2385.             gpath->params[4] = -width / 2;
  2386.             gpath->params[5] = height / 2;
  2387.             gpath->params[6] = -width / 2;
  2388.             gpath->params[7] = -height / 2;
  2389.             gpath->params[8] =width / 2;
  2390.             gpath->params[9] = -height / 2;
  2391.             break;
  2392.         case HEXAGON_NODETYPE:
  2393.             gpath->params[0] = pos.x + placer.hexagon[0];
  2394.             gpath->params[1] = pos.y;
  2395.             gpath->params[2] = width - 2 * placer.hexagon[0];
  2396.             gpath->params[3] = 0;
  2397.             gpath->params[4] = placer.hexagon[0];
  2398.             gpath->params[5] = height / 2;
  2399.             gpath->params[6] = -placer.hexagon[0];
  2400.             gpath->params[7] = height / 2;
  2401.             gpath->params[8] =-width + 2 * placer.hexagon[0];
  2402.             gpath->params[9] = 0;
  2403.             gpath->params[10] =-placer.hexagon[0];
  2404.             gpath->params[11] = -height / 2;
  2405.             gpath->params[12] = placer.hexagon[0];
  2406.             gpath->params[13] = -height / 2;
  2407.             break;
  2408.         default:
  2409.             break;    
  2410.     }
  2411.     return self;
  2412. }
  2413.  
  2414. - calcWidthHeight
  2415. {
  2416.     NXCoord v1,v2;
  2417.     NXSize textSize;
  2418.  
  2419.     [textCell calcCellSize:&textSize];
  2420.     switch(pathKind){
  2421.         case RECT_NODETYPE:
  2422.             width = textSize.width + 2 * placer.rect[0];
  2423.             height = textSize.height + 2 * placer.rect[1];
  2424.             break;
  2425.         case ROUNDRECT_NODETYPE:
  2426.             width = textSize.width + 2 * placer.roundRect[0];
  2427.             height = textSize.height + 2 * placer.roundRect[1];
  2428.             break;
  2429.         case ELLIPSE_NODETYPE:
  2430.             height = textSize.height + 2 * placer.ellipse[0];
  2431.             v1 = placer.ellipse[0] - placer.ellipse[1];
  2432.             v2 = placer.ellipse[0] + height + placer.ellipse[1];
  2433.             width = (textSize.width / 2 + placer.ellipse[1]) * height / sqrt(v1 * v2);
  2434.             break;
  2435.         case RHOMB_NODETYPE:
  2436.             height = textSize.height + 2 * placer.rhomb[0];
  2437.             v1 = placer.rhomb[0] - placer.rhomb[1];
  2438.             width = (textSize.width / 2 + placer.rhomb[1]) * height / v1;
  2439.             break;
  2440.         case HEXAGON_NODETYPE:
  2441.             width = textSize.width + 2 * placer.hexagon[0];
  2442.             height = textSize.height + 2 * placer.hexagon[1];
  2443.             break;
  2444.         default:
  2445.             break;
  2446.     }
  2447.     deltaText.x = (width - textSize.width) / 2;
  2448.     deltaText.y = (height - textSize.height) / 2;
  2449.     deltaLink.x = width / 2;
  2450.     deltaLink.y = height / 2;
  2451.     NXSetRect(&textCellFrame, 0, 0, textSize.width, textSize.height);
  2452.     return self;
  2453. }
  2454.  
  2455. - internalCopyFromZone:(NXZone *)zone parent:aParent
  2456. {
  2457.        Tree *theCopy;
  2458.  
  2459.        theCopy = [super copyFromZone:zone];
  2460.     theCopy->contour.upper.head = contour.upper.tail = NULL;
  2461.     theCopy->contour.lower.head = contour.lower.tail = NULL;
  2462.     theCopy->updateLayout = YES;
  2463.     theCopy->parent = aParent;
  2464.     if(child)
  2465.         theCopy->child = [child internalCopyFromZone:zone parent:theCopy];
  2466.     else
  2467.         theCopy->child = nil;
  2468.     if(sibling)
  2469.         theCopy->sibling = [sibling internalCopyFromZone:zone parent:aParent];
  2470.     else
  2471.         theCopy->sibling =    nil;
  2472.     theCopy->selected = NO;
  2473.     theCopy->gpath = [gpath copyFromZone:zone];
  2474.     theCopy->nodeDescription = [nodeDescription copyFromZone:zone];;
  2475.     theCopy->delegate = nil;
  2476.     if(verknuepfung)
  2477.         theCopy->verknuepfung = [verknuepfung copyFromZone:zone];
  2478.     else
  2479.         theCopy->verknuepfung = nil;
  2480.     theCopy->textCell = [textCell copyFromZone:zone];
  2481.        return theCopy;
  2482. }
  2483.  
  2484. - calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint
  2485. {
  2486.     NXPoint middle,l1,l2;
  2487.     float s,xm,ym,x0,y0,r,ausdruck,a,b;
  2488.     
  2489.     middle.x = pos.x + width / 2;
  2490.     middle.y = pos.y + height / 2;
  2491.     if(aPoint->y == middle.y){
  2492.         *alpha = 0;
  2493.         ip->y = middle.y;
  2494.         if(aPoint->x > middle.x)
  2495.             ip->x = pos.x + width;
  2496.         else
  2497.             ip->x = pos.x;
  2498.         return self;
  2499.     }
  2500.     if(aPoint->x == middle.x){
  2501.         ip->x = middle.x;
  2502.         if(aPoint->y > middle.y){
  2503.             ip->y = pos.y + height;
  2504.             *alpha = -90;
  2505.         } else {
  2506.             ip->y = pos.y;
  2507.             *alpha = 90;
  2508.         }
  2509.         return self;
  2510.     }
  2511.     s = (aPoint->y - middle.y) / (aPoint->x - middle.x);
  2512.     *alpha = 57.29577951 * atan(s);
  2513.     if(aPoint->y > middle.y){
  2514.         if(aPoint->x > middle.x){                         // 4. quadrant
  2515.             switch(pathKind){
  2516.                 case RECT_NODETYPE:
  2517.                     l1.x = middle.x;
  2518.                     l1.y = l2.y = pos.y + height;
  2519.                     l2.x = pos.x + width;
  2520.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2521.                         ip->y = l1.y;
  2522.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2523.                     } else {
  2524.                         ip->x = l2.x;
  2525.                         ip->y = middle.y + s * (ip->x - middle.x);
  2526.                     }
  2527.                 break;
  2528.                 case ROUNDRECT_NODETYPE:
  2529.                     l1.x = middle.x;
  2530.                     l1.y = pos.y + height;
  2531.                     l2.x = pos.x + width - height / 3;
  2532.                     l2.y = pos.y + height;
  2533.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2534.                         ip->y = l1.y;
  2535.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2536.                     } else {
  2537.                         l1.x = l2.x = pos.x + width;
  2538.                         l1.y = middle.y;
  2539.                         l2.y = pos.y + height - height / 3;
  2540.                         if(straddles(&l1, &l2, &middle, aPoint)){
  2541.                             ip->x = l2.x;
  2542.                             ip->y = middle.y + s * (ip->x - middle.x);    
  2543.                         } else {
  2544.                             r = height / 3;
  2545.                             x0 = middle.x;
  2546.                             y0 = middle.y;
  2547.                             xm = pos.x + width - r;
  2548.                             ym = pos.y + height - r;
  2549.                             ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
  2550.                                           2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
  2551.                                           pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
  2552.                             ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
  2553.                             ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
  2554.                             if(ip->y < ym){
  2555.                                 ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
  2556.                                 ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
  2557.                             }
  2558.                         }
  2559.                     }     
  2560.                 break;
  2561.                 case ELLIPSE_NODETYPE:
  2562.                     x0 = middle.x;
  2563.                     y0 = middle.y;
  2564.                     a = width / 2;
  2565.                     b = height / 2;
  2566.                     ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
  2567.                     ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2568.                     if(ip->y < y0){
  2569.                         ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
  2570.                         ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2571.                     }
  2572.                 break;
  2573.                 case RHOMB_NODETYPE:
  2574.                     l1.x = middle.x;
  2575.                     l1.y = pos.y + height;
  2576.                     l2.x = pos.x + width;
  2577.                     l2.y = middle.y;
  2578.                     ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2579.                     ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2580.                     ip->y = middle.y + s * (ip->x - middle.x);
  2581.                 break;
  2582.                 case HEXAGON_NODETYPE:
  2583.                     l1.x = middle.x;
  2584.                     l1.y = l2.y = pos.y + height;
  2585.                     l2.x = pos.x + width - placer.hexagon[0];
  2586.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2587.                         ip->y = l1.y;
  2588.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2589.                     } else {
  2590.                         l1.x = pos.x + width;
  2591.                         l1.y = middle.y;
  2592.                         ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2593.                         ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2594.                         ip->y = middle.y + s * (ip->x - middle.x);
  2595.                     }
  2596.                 break;
  2597.                 default:
  2598.                 break;
  2599.             }
  2600.         } else {                                         // 3. quadrant
  2601.             switch(pathKind){
  2602.                 case RECT_NODETYPE:
  2603.                     l1.x = middle.x;
  2604.                     l1.y = l2.y = pos.y + height;
  2605.                     l2.x = pos.x;
  2606.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2607.                         ip->y = l1.y;
  2608.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2609.                     } else {
  2610.                         ip->x = l2.x;
  2611.                         ip->y = middle.y + s * (ip->x - middle.x);
  2612.                     }
  2613.                 break;
  2614.                 case ROUNDRECT_NODETYPE:
  2615.                     l1.x = middle.x;
  2616.                     l1.y = pos.y + height;
  2617.                     l2.x = pos.x + height / 3;
  2618.                     l2.y = pos.y + height;
  2619.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2620.                         ip->y = l1.y;
  2621.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2622.                     } else {
  2623.                         l1.x = l2.x = pos.x;
  2624.                         l1.y = middle.y;
  2625.                         l2.y = pos.y + height - height / 3;
  2626.                         if(straddles(&l1, &l2, &middle, aPoint)){
  2627.                             ip->x = l2.x;
  2628.                             ip->y = middle.y + s * (ip->x - middle.x);    
  2629.                         } else {
  2630.                             r = height / 3;
  2631.                             x0 = middle.x;
  2632.                             y0 = middle.y;
  2633.                             xm = pos.x + r;
  2634.                             ym = pos.y + height - r;
  2635.                             ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
  2636.                                           2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
  2637.                                           pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
  2638.                             ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
  2639.                             ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
  2640.                             if(ip->y < ym){
  2641.                                 ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
  2642.                                 ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
  2643.                             }
  2644.                         }
  2645.                     }
  2646.                 break;
  2647.                 case ELLIPSE_NODETYPE:
  2648.                     x0 = middle.x;
  2649.                     y0 = middle.y;
  2650.                     a = width / 2;
  2651.                     b = height / 2;
  2652.                     ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
  2653.                     ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2654.                     if(ip->y < y0){
  2655.                         ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
  2656.                         ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2657.                     }
  2658.                 break;
  2659.                 case RHOMB_NODETYPE:
  2660.                     l1.x = middle.x;
  2661.                     l1.y = pos.y + height;
  2662.                     l2.x = pos.x;
  2663.                     l2.y = middle.y;
  2664.                     ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2665.                     ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2666.                     ip->y = middle.y + s * (ip->x - middle.x);
  2667.                 break;
  2668.                 case HEXAGON_NODETYPE:
  2669.                     l1.x = middle.x;
  2670.                     l1.y = l2.y = pos.y + height;
  2671.                     l2.x = pos.x + placer.hexagon[0];
  2672.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2673.                         ip->y = l1.y;
  2674.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2675.                     } else {
  2676.                         l1.x = pos.x;
  2677.                         l1.y = middle.y;
  2678.                         ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2679.                         ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2680.                         ip->y = middle.y + s * (ip->x - middle.x);
  2681.                     }
  2682.                 break;
  2683.                 default:
  2684.                 break;
  2685.             }
  2686.         }
  2687.     } else {
  2688.         if(aPoint->x > middle.x){                         // 1. quadrant
  2689.             switch(pathKind){
  2690.                 case RECT_NODETYPE:
  2691.                     l1.x = middle.x;
  2692.                     l1.y = l2.y = pos.y;
  2693.                     l2.x = pos.x + width;
  2694.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2695.                         ip->y = l1.y;
  2696.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2697.                     } else {
  2698.                         ip->x = l2.x;
  2699.                         ip->y = middle.y + s * (ip->x - middle.x);
  2700.                     }
  2701.                 break;
  2702.                 case ROUNDRECT_NODETYPE:
  2703.                     l1.x = middle.x;
  2704.                     l1.y = pos.y;
  2705.                     l2.x = pos.x + width - height / 3;
  2706.                     l2.y = pos.y;
  2707.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2708.                         ip->y = l1.y;
  2709.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2710.                     } else {
  2711.                         l1.x = l2.x = pos.x + width;
  2712.                         l1.y = middle.y;
  2713.                         l2.y = pos.y + height / 3;
  2714.                         if(straddles(&l1, &l2, &middle, aPoint)){
  2715.                             ip->x = l2.x;
  2716.                             ip->y = middle.y + s * (ip->x - middle.x);    
  2717.                         } else {
  2718.                             r = height / 3;
  2719.                             x0 = middle.x;
  2720.                             y0 = middle.y;
  2721.                             xm = pos.x + width - r;
  2722.                             ym = pos.y + r;
  2723.                             ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
  2724.                                           2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
  2725.                                           pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
  2726.                             ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
  2727.                             ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
  2728.                             if(ip->y > ym){
  2729.                                 ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
  2730.                                 ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
  2731.                             }
  2732.                         }
  2733.                     }
  2734.                 break;
  2735.                 case ELLIPSE_NODETYPE:
  2736.                     x0 = middle.x;
  2737.                     y0 = middle.y;
  2738.                     a = width / 2;
  2739.                     b = height / 2;
  2740.                     ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
  2741.                     ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2742.                     if(ip->y > y0){
  2743.                         ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
  2744.                         ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2745.                     }
  2746.                 break;
  2747.                 case RHOMB_NODETYPE:
  2748.                     l1.x = middle.x;
  2749.                     l1.y = pos.y;
  2750.                     l2.x = pos.x + width;
  2751.                     l2.y = middle.y;
  2752.                     ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2753.                     ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2754.                     ip->y = middle.y + s * (ip->x - middle.x);
  2755.                 break;
  2756.                 case HEXAGON_NODETYPE:
  2757.                     l1.x = middle.x;
  2758.                     l1.y = l2.y = pos.y;
  2759.                     l2.x = pos.x + width - placer.hexagon[0];
  2760.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2761.                         ip->y = l1.y;
  2762.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2763.                     } else {
  2764.                         l1.x = pos.x + width;
  2765.                         l1.y = middle.y;
  2766.                         ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2767.                         ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2768.                         ip->y = middle.y + s * (ip->x - middle.x);
  2769.                     }
  2770.                 break;
  2771.                 default:
  2772.                 break;
  2773.             }
  2774.         } else {                                         // 2. quadrant
  2775.             switch(pathKind){
  2776.                 case RECT_NODETYPE:
  2777.                     l1.x = middle.x;
  2778.                     l1.y = l2.y = pos.y;
  2779.                     l2.x = pos.x;
  2780.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2781.                         ip->y = l1.y;
  2782.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2783.                     } else {
  2784.                         ip->x = l2.x;
  2785.                         ip->y = middle.y + s * (ip->x - middle.x);
  2786.                     }
  2787.                 break;
  2788.                 case ROUNDRECT_NODETYPE:
  2789.                     l1.x = middle.x;
  2790.                     l1.y = pos.y;
  2791.                     l2.x = pos.x + height / 3;
  2792.                     l2.y = pos.y;
  2793.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2794.                         ip->y = l1.y;
  2795.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2796.                     } else {
  2797.                         l1.x = l2.x = pos.x;
  2798.                         l1.y = middle.y;
  2799.                         l2.y = pos.y + height / 3;
  2800.                         if(straddles(&l1, &l2, &middle, aPoint)){
  2801.                             ip->x = l2.x;
  2802.                             ip->y = middle.y + s * (ip->x - middle.x);    
  2803.                         } else {
  2804.                             r = height / 3;
  2805.                             x0 = middle.x;
  2806.                             y0 = middle.y;
  2807.                             xm = pos.x + r;
  2808.                             ym = pos.y + r;
  2809.                             ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
  2810.                                           2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
  2811.                                           pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
  2812.                             ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
  2813.                             ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
  2814.                             if(ip->y > ym){
  2815.                                 ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
  2816.                                 ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
  2817.                             }
  2818.                         }
  2819.                     }
  2820.                 break;
  2821.                 case ELLIPSE_NODETYPE:
  2822.                     x0 = middle.x;
  2823.                     y0 = middle.y;
  2824.                     a = width / 2;
  2825.                     b = height / 2;
  2826.                     ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
  2827.                     ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2828.                     if(ip->y > y0){
  2829.                         ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
  2830.                         ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
  2831.                     }
  2832.                 break;
  2833.                 case RHOMB_NODETYPE:
  2834.                     l1.x = middle.x;
  2835.                     l1.y = pos.y;
  2836.                     l2.x = pos.x;
  2837.                     l2.y = middle.y;
  2838.                     ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2839.                     ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2840.                     ip->y = middle.y + s * (ip->x - middle.x);
  2841.                 break;
  2842.                 case HEXAGON_NODETYPE:
  2843.                     l1.x = middle.x;
  2844.                     l1.y = l2.y = pos.y;
  2845.                     l2.x = pos.x + placer.hexagon[0];
  2846.                     if(straddles(&l1, &l2, &middle, aPoint)){
  2847.                         ip->y = l1.y;
  2848.                         ip->x = middle.x + (ip->y - middle.y) / s;
  2849.                     } else {
  2850.                         l1.x = pos.x;
  2851.                         l1.y = middle.y;
  2852.                         ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
  2853.                         ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
  2854.                         ip->y = middle.y + s * (ip->x - middle.x);
  2855.                     }
  2856.                 break;
  2857.                 default:
  2858.                 break;
  2859.             }
  2860.         }
  2861.     }
  2862.     return self;
  2863. }
  2864.         
  2865. @end
  2866.  
  2867. //************************************************************************
  2868. //  static c functions implementation
  2869.  
  2870. static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl,
  2871.                                 NXCoord *low, NXCoord *loh)
  2872. {
  2873.     NXCoord minX, minY, maxX, maxY, x, y;
  2874.     Polyline *dummy;
  2875.  
  2876.     minX = minY = 9999;
  2877.     maxX = maxY = -9999;
  2878.     dummy = aPol->upper.head;
  2879.     x = ipu->x;
  2880.     y = ipu->y;
  2881.     minX = MIN(minX, x);
  2882.     minY = MIN(minY, y);
  2883.     maxX = MAX(maxX, x);
  2884.     maxY = MAX(maxY, y);
  2885.     while(dummy){
  2886.         x += dummy->dx;
  2887.         y += dummy->dy;
  2888.         minX = MIN(minX, x);
  2889.         minY = MIN(minY, y);
  2890.         maxX = MAX(maxX, x);
  2891.         maxY = MAX(maxY, y);
  2892.         dummy = dummy->link;
  2893.     } 
  2894.     dummy = aPol->lower.head;
  2895.     x = ipl->x;
  2896.     y = ipl->y;
  2897.     minX = MIN(minX, x);
  2898.     minY = MIN(minY, y);
  2899.     maxX = MAX(maxX, x);
  2900.     maxY = MAX(maxY, y);
  2901.     while(dummy){
  2902.         x += dummy->dx;
  2903.         y += dummy->dy;
  2904.         minX = MIN(minX, x);
  2905.         minY = MIN(minY, y);
  2906.         maxX = MAX(maxX, x);
  2907.         maxY = MAX(maxY, y);
  2908.         dummy = dummy->link;
  2909.     }
  2910.     NXSetRect(rect, minX, minY, maxX - minX, maxY - minY);
  2911.     *loh = ipu->y - minY;
  2912.     *low = ipu->x - minX;
  2913. }
  2914.  
  2915. static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone)
  2916. {
  2917.     NXCoord x,y,total,d;
  2918.     Polyline *lower,*upper,*b;
  2919.  
  2920.     x = y = total = 0;
  2921.     upper = c1->lower.head;
  2922.     lower = c2->upper.head;
  2923.     while(lower && upper){
  2924.         d = offset(x,y,lower->dx,lower->dy,upper->dx,upper->dy);
  2925.         y += d;
  2926.         total += d;
  2927.         if(x + lower->dx <= upper->dx){
  2928.                y += lower->dy;
  2929.                x += lower->dx;
  2930.                lower = lower->link;
  2931.         } else {
  2932.                y -= upper->dy;
  2933.                x -= upper->dx;
  2934.                upper = upper->link;
  2935.         }  
  2936.     }
  2937.     if(lower){
  2938.         b = bridge(c1->upper.tail,lower,0,0,x,y,zone);
  2939.         c1->upper.tail = (b->link) ? c2->upper.tail : b;
  2940.         c1->lower.tail = c2->lower.tail;
  2941.         partialFreePolyline(c2->upper.head, lower->link);
  2942.         freePolyline(c1->lower.head);
  2943.     } else {
  2944.         b = bridge(c2->lower.tail,upper,x,y,0,0,zone);
  2945.         if(!b->link)
  2946.             c1->lower.tail = b;
  2947.         partialFreePolyline(c1->lower.head,upper->link);
  2948.         freePolyline(c2->upper.head);    
  2949.     }
  2950.     c1->lower.head = c2->lower.head;
  2951.     return total;
  2952. }
  2953.  
  2954. static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2,
  2955.                       NXCoord b1,NXCoord b2)
  2956. {
  2957.     NXCoord d,s,t;
  2958.  
  2959.     if(b1 <= p1 || p1 + a1 <= 0)
  2960.          return 0;
  2961.     t = b1 * a2 - a1 * b2;
  2962.     if(t > 0){
  2963.         if(p1 < 0)
  2964.             s = p1 * a2,d = s / a1 - p2;
  2965.         else if(p1 > 0)
  2966.             s = p1 * b2,d = s / b1 - p2;
  2967.         else
  2968.             d = -p2;
  2969.     } else if(b1 < p1 + a1)
  2970.         s = (b1 - p1) * a2,d = b2 - (p2 + s / a1);
  2971.     else if(b1 > p1 + a1)
  2972.         s = (a1 + p1) * b2, d = s / b1 - (p2 + a2);
  2973.     else
  2974.         d = b2 - (p2 + a2);
  2975.     return MAX(0,d);
  2976. }
  2977.  
  2978. static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1,
  2979.                        NXCoord x2,NXCoord y2, NXZone *zone)
  2980. {
  2981.     NXCoord dx,dy,s;
  2982.     Polyline *r;
  2983.    
  2984.     dx = x2 + line2->dx - x1;
  2985.     if(line2->dx == 0)
  2986.        dy = line2->dy;
  2987.     else
  2988.        s = dx * line2->dy,dy = s / line2->dx;
  2989.     r = allocLine(dx,dy,line2->link,zone);
  2990.     line1->link = allocLine(0,y2 + line2->dy -dy - y1,r,zone);
  2991.     return r;
  2992. }
  2993.  
  2994. static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone)
  2995. {
  2996.     Polyline *theAlloced;
  2997.  
  2998.     theAlloced = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
  2999.     theAlloced->dx = aX;
  3000.     theAlloced->dy = aY;
  3001.     theAlloced->link = aLink; 
  3002.     return theAlloced;
  3003. }
  3004.  
  3005. static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone)
  3006. {
  3007.     Polyline *aLine,*l,*result;
  3008.     
  3009.     l = aSource;
  3010.     aLine = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
  3011.     aLine->dx = l->dx;
  3012.     aLine->dy = l->dy;
  3013.     aLine->link = NULL; 
  3014.     result = aLine;
  3015.     l = l->link;
  3016.     while(l){
  3017.         aLine->link = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
  3018.         aLine = aLine->link;
  3019.         aLine->dx = l->dx;
  3020.         aLine->dy = l->dy;
  3021.         aLine->link = NULL;
  3022.         l = l->link;
  3023.     }
  3024.     *aTail = aLine;
  3025.     return result;
  3026. }
  3027.  
  3028. static void freePolyline(Polyline *aLine)
  3029. {
  3030.     Polyline *index,*trailer;
  3031.     
  3032.     trailer = aLine;
  3033.     index = aLine;
  3034.     while(index){
  3035.         index = index->link;
  3036.         NX_FREE(trailer);
  3037.         trailer = index;
  3038.     }
  3039. }
  3040.  
  3041. static void partialFreePolyline(Polyline *aLine, Polyline *toEnd)
  3042. {
  3043.     Polyline *index,*trailer;
  3044.     
  3045.     trailer = aLine;
  3046.     index = aLine;
  3047.     while(index && index != toEnd){
  3048.         index = index->link;
  3049.         NX_FREE(trailer);
  3050.         trailer = index;
  3051.     }
  3052. }
  3053.  
  3054. static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4)
  3055. {
  3056.     float crossP1, crossP2;
  3057.     
  3058.     crossP1 = (p3->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p3->y - p1->y); 
  3059.     crossP2 = (p4->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p4->y - p1->y);
  3060.     if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0)){
  3061.         crossP1 = (p1->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p1->y - p3->y); 
  3062.         crossP2 = (p2->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p2->y - p3->y);
  3063.         if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0))
  3064.             return YES;
  3065.         else
  3066.             return NO;
  3067.     } else
  3068.         return NO;
  3069.     
  3070.     
  3071.     
  3072.     
  3073.     
  3074.