home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / DevTools / ClassBuilder / Source / GlyphView.m < prev    next >
Encoding:
Text File  |  1993-01-25  |  14.9 KB  |  476 lines

  1. #pragma .h #import  <appkit/View.h> 
  2. #pragma .h #import "Glyph.h"
  3. #import "GlyphView.h"
  4. #import <appkit/Application.h>
  5. #import <appkit/Window.h>
  6. #import <appkit/NXCursor.h>
  7. #import <dpsclient/psops.h>
  8. #import <dpsclient/wraps.h>
  9. #import <c.h>
  10. #import "/local/grd/CB/MenuManager.h"
  11. #import <appkit/ScrollView.h>
  12. #import <appkit/publicWraps.h>
  13. #import <objc/List.h>
  14.  
  15. static id glyphViewList ;
  16.  
  17. @implementation GlyphView: View
  18. { id fGView, bGView, scrollView ;
  19.   Glyph *rootGlyph, *targetGlyph, *superGlyph ;
  20.   int fGGState, bGGState ;
  21.   NXRect targetFrame ; // targetGlyph's frame in my coord sys
  22.   NXRect clipRect ;    // clipping rectangle around targetGlyph
  23.   NXRect visibleRect ; // visible portion of rootGlyph
  24.   NXPoint mouseOffset ; // distance from mouse hit to target's origin
  25.   BOOL rootIsTarget, isAWindow ;
  26. }
  27.  
  28. + glyphViewList ;
  29. { // return the list of glyph views
  30.   if(glyphViewList == nil)
  31.     glyphViewList = [[List alloc] init] ;
  32.   return glyphViewList ;
  33. }
  34.  
  35. -blowUp ;
  36. // Provide visual feedback that the target glyph is
  37. // dying, then kill it.
  38. { NXRect aRect ;
  39.   float dx, dy ;
  40.   id aWin ;
  41.   int i , knt = 20 ;
  42.   // shrink window to nothing
  43.   aWin = [fGView window] ;
  44.   [aWin orderFront: self] ; // may be offscreen
  45.   [aWin getFrame: &aRect] ;
  46.   dx = aRect.size.width / (float) knt ;
  47.   dy = aRect.size.height / (float) knt ;
  48.   for(i = 0 ; i < knt ; i++)
  49.   { [aWin sizeWindow: aRect.size.width :aRect.size.height] ;
  50.     NXPing() ;
  51.     aRect.size.width -= dx ;
  52.     aRect.size.height -= dy ;
  53.   }
  54.   [aWin orderOut: self] ;
  55.   [targetGlyph free] ; // actually, doesn't know how to free yet!
  56.   targetGlyph = nil ;
  57.   return self ; 
  58. }
  59.  
  60.  
  61. -clipRect: (NXRect *) aRect toGlyph: (Glyph *) aGlyph ;
  62. // set aRect to the clipping rectangle imposed by aGlyph.
  63. // This involves calculating intersection rectangles 
  64. // back to the rootGlyph.  Just calculates the clipping
  65. // rectangle; does not actually set it.
  66. { if(aGlyph->flags.isRoot)
  67.   { aRect->origin.x = aGlyph->frame.origin.x ;
  68.     aRect->origin.y = aGlyph->frame.origin.y ;
  69.     aRect->size.width = aGlyph->frame.size.width ;
  70.     aRect->size.height = aGlyph->frame.size.height ;
  71.     return self ;
  72.   }
  73.   else
  74.   { NXRect bRect = {{0.0,0.0},{0.0,0.0}} ;
  75.     [self clipRect: aRect toGlyph: aGlyph->ancestor] ;
  76.     bRect.size.height = aGlyph->frame.size.height ;
  77.     bRect.size.width = aGlyph->frame.size.width ;
  78.     [aGlyph convertToRootGlyph: &bRect.origin] ;
  79.     NXIntersectionRect(&bRect,aRect) ;
  80.   } 
  81.   return self ; 
  82. }
  83.  
  84. -convertToScreen: (NXPoint *) aPnt ;
  85. // convert aPnt from my coord sys to screen coord sys
  86. {  [self convertPoint: aPnt toView: nil] ;
  87.    [window convertBaseToScreen: aPnt] ;
  88.    return self ;
  89. }
  90.  
  91.   
  92. -drawSelf:(const NXRect *)rects :(int)rectCount ;
  93. { // drawSelf: does the displaying when my window
  94.   // is first opened, when its resized, when its
  95.   // scrolled, or when its unminiaturized.
  96.   // composite background 
  97.   PScomposite(rects->origin.x,
  98.          rects->origin.y,
  99.          rects->size.width,
  100.          rects->size.height,
  101.          bGGState,
  102.          rects->origin.x, 
  103.          rects->origin.y,
  104.          NX_COPY) ;
  105.   // and, if necessary, the foreground
  106.   if(targetGlyph && NXIntersectsRect(rects, &targetFrame))
  107.   { NXRect unionRect ;
  108.     float x,y ;
  109.     unionRect = targetFrame ;
  110.     NXUnionRect(rects,&unionRect) ;
  111.     x = unionRect.origin.x  - targetFrame.origin.x ;
  112.     y = unionRect.origin.y  - targetFrame.origin.y ;
  113.     PScomposite(x,
  114.          y,
  115.          unionRect.size.width,
  116.          unionRect.size.height,
  117.          fGGState,
  118.          unionRect.origin.x, 
  119.          unionRect.origin.y,
  120.          NX_SOVER) ;
  121.   }
  122.   return self ;
  123. }
  124.  
  125. - handOff: (NXPoint *) aPnt ;
  126. { // Attempts to handOff: the current targetGlyph to another
  127.   // glyphView, if aPnt (the current mouse point in screen coords)
  128.   // is within that view.  Deletes the target if it fails.  In
  129.   // either case, sets the targetGlyph to nil.
  130.   NXRect visRect ;
  131.   NXPoint thePnt ;
  132.   int found, gWinNum, fGWinNum, targetWin, i, knt ;
  133.   id theList, theView  ;
  134.   NXConvertWinNumToGlobal([[fGView window] windowNum],&fGWinNum) ;
  135.   [[fGView window] orderOut: self] ; // don't find the fG window
  136.   PSfindwindow(aPnt->x,aPnt->y,NX_ABOVE,0,
  137.         &thePnt.x,&thePnt.y,&gWinNum,&found) ;
  138.   if(!found)
  139.     return nil ;
  140.   NXConvertGlobalToWinNum(gWinNum, &targetWin) ; 
  141.   theList = [GlyphView glyphViewList] ;
  142.   knt = [theList count] ;
  143.   for(i = 0 ; i < knt ; i++)
  144.   { if(( [[(theView = [theList objectAt: i]) window] windowNum]) == targetWin)
  145.     { // we've got a candidate...
  146.       NXPoint viewPnt ;
  147.       viewPnt = *aPnt ;
  148.       [[theView window] convertScreenToBase: &viewPnt] ;
  149.       [theView convertPoint: &viewPnt fromView: nil] ;
  150.       [[[theView superview] superview] getDocVisibleRect: &visRect] ;
  151.       if(NXPointInRect(&viewPnt,&visRect)) // give it away...
  152.       { viewPnt.x -= mouseOffset.x ;
  153.         viewPnt.y -= mouseOffset.y ;
  154.         [theView receive: targetGlyph at: &viewPnt] ;
  155.         targetGlyph = nil ;
  156.         return self ;
  157.       }
  158.       else
  159.         [self blowUp] ;
  160.       break ;
  161.     }
  162.   }
  163.   return nil ;
  164. }
  165.  
  166.  
  167. - mouseDown: (NXEvent *) anEvent ;
  168. { Glyph *tg ;
  169.   BOOL newTarget = NO ;
  170.   [self lockFocus] ;
  171.   [self convertPoint: &anEvent->location fromView: nil] ;
  172.   tg = [rootGlyph hitTest: &anEvent->location] ;
  173.   if(tg != targetGlyph) // new target
  174.   {  [self newTarget: tg] ;
  175.      newTarget = YES ;
  176.   }
  177.   if(tg == rootGlyph)
  178.   { rootIsTarget = YES ; 
  179.     return self ; 
  180.   }
  181.   rootIsTarget = NO ;
  182.    // set the clipping rectangle
  183.   PSgsave() ;
  184.   superGlyph = tg->ancestor ;
  185.   [self clipRect: &clipRect toGlyph: superGlyph] ;
  186.   NXRectClip(&clipRect) ;
  187.  // remove target glyph from TTree
  188.   [targetGlyph unlink] ;
  189.   // calculate new mouse offset
  190.   mouseOffset.x = anEvent->location.x - targetFrame.origin.x ;
  191.   mouseOffset.y = anEvent->location.y - targetFrame.origin.y ;
  192.   isAWindow = false ; // initially, glyph must be in our view
  193.   // finally, highlight the new target
  194.   if(newTarget)
  195.   { [self lockFocus] ;
  196.     PSsetgray(0.666) ;
  197.     PScompositerect(targetFrame.origin.x,targetFrame.origin.y,
  198.        targetFrame.size.width,targetFrame.size.height, NX_PLUS) ; 
  199.     [self unlockFocus] ;
  200.    [window flushWindow] ;
  201.   }
  202.   return self ;
  203. }
  204.  
  205.  
  206. - mouseDragged: (NXEvent *) anEvent ;
  207. { Glyph *aGlyph ;
  208.   if(rootIsTarget)
  209.     return self ; // can't drag the root glyph
  210.   [self convertPoint: &anEvent->location fromView: nil] ;
  211.    // see if we've dragged out of our window
  212.   if(!isAWindow) // i.e. if glyph is in our view...
  213.   {  // erase our old image
  214.      PScomposite(targetFrame.origin.x, targetFrame.origin.y,
  215.      targetFrame.size.width, targetFrame.size.height,
  216.      bGGState, targetFrame.origin.x, targetFrame.origin.y,NX_COPY) ;
  217.     if(!NXPointInRect(&anEvent->location,&visibleRect)) // just dragged out of view...
  218.     { NXPoint winPnt ; // turn into a "travelling window"
  219.       winPnt.x = targetFrame.origin.x ;
  220.       winPnt.y = targetFrame.origin.y ;
  221.       [self convertToScreen: &winPnt] ;
  222.       [[fGView window] moveTo: winPnt.x :winPnt.y] ;
  223.       [[fGView window] orderFront: self] ;
  224.       isAWindow = YES ;
  225.     }
  226.   }
  227.   else // glyph dragged out of our view, so is a "travelling window"
  228.   { if(NXPointInRect(&anEvent->location,&visibleRect)) // dragged back in?
  229.     { [[fGView window] orderOut: self] ;
  230.       isAWindow = NO ;
  231.     }
  232.     else // glyph was, and still is, a "travelling window"
  233.    {  NXPoint winPnt ; // move the "travelling window"
  234.       winPnt.x = targetFrame.origin.x ;
  235.       winPnt.y = targetFrame.origin.y ;
  236.       [self convertPoint: &winPnt toView: nil] ;
  237.       [window convertBaseToScreen:&winPnt] ;
  238.       [[fGView window] moveTo: winPnt.x :winPnt.y] ;
  239.     }    
  240.   }
  241.   // update the target's location
  242.   targetFrame.origin.x = anEvent->location.x - mouseOffset.x ;
  243.   targetFrame.origin.y = anEvent->location.y - mouseOffset.y ;
  244.   if(!isAWindow) // if not a "travelling window"....
  245.   { // see if clip rectangle must change
  246.     aGlyph = [rootGlyph hitTest: &anEvent->location] ;
  247.     if(aGlyph && (aGlyph != superGlyph)) 
  248.     { // must reset the clip rectangle
  249.       superGlyph = aGlyph ;
  250.       PSgrestore() ;
  251.       PSgsave() ;
  252.       [self clipRect: &clipRect toGlyph: superGlyph] ;
  253.       NXRectClip(&clipRect) ;
  254.     }
  255.     // draw our new image
  256.      PScomposite(0.0, 0.0,targetFrame.size.width,targetFrame.size.height,
  257.       fGGState,targetFrame.origin.x, targetFrame.origin.y,NX_PLUS) ; 
  258.     // finally, highlight the new target
  259.     [self lockFocus] ;
  260.     PSsetgray(0.666) ;
  261.     PScompositerect(targetFrame.origin.x,targetFrame.origin.y,
  262.        targetFrame.size.width,targetFrame.size.height, NX_PLUS) ; 
  263.     [self unlockFocus] ;
  264.     [window flushWindow] ;
  265.   }
  266.   return self ;
  267. }
  268.  
  269. - mouseUp: (NXEvent *) anEvent ;
  270. { if(rootIsTarget)
  271.     return self ;
  272.   PSgrestore() ;
  273.   [self unlockFocus] ;
  274.   if(isAWindow)
  275.   { [[self window] convertBaseToScreen: &anEvent->location] ;
  276.     [self handOff: &anEvent->location] ; // hand it to another view, or the void!
  277.     return self ;
  278.   }
  279.   [self convertPoint: &anEvent->location fromView: nil] ;
  280.   // relink the target
  281.   // Is the next hitTest necessary?
  282.   superGlyph = [rootGlyph hitTest: &anEvent->location] ;
  283.   [superGlyph convertFromRootGlyph: &anEvent->location] ;
  284.   [targetGlyph moveTo: anEvent->location.x - mouseOffset.x
  285.                      : anEvent->location.y - mouseOffset.y ] ;
  286.   [superGlyph plant: targetGlyph] ;
  287.   return self ;
  288. }
  289.  
  290.  
  291. - newTarget: (Glyph *) tg ;
  292. { // This is called whenever a new target is selected.
  293.   // (A smarter version
  294.   // would know if the fg was "growing" or "shrinking".)
  295.   // To repair the background, first composite the fGrnd 
  296.   // into the background...
  297.   [bGView lockFocus] ;
  298.   if(targetGlyph != nil) 
  299.   { // composite visible portion of old target into bGrnd
  300.     NXIntersectionRect(&clipRect,&targetFrame) ;
  301.     PScomposite(0.0,0.0,
  302.       targetFrame.size.width,targetFrame.size.height,fGGState,
  303.       targetFrame.origin.x, targetFrame.origin.y, NX_PLUS) ;
  304.     targetGlyph->flags.isTarget = FALSE ;
  305.    // repair the area in offscreen window
  306.    [self lockFocus] ;
  307.    PScomposite(targetFrame.origin.x,targetFrame.origin.y,
  308.       targetFrame.size.width,targetFrame.size.height,bGGState,
  309.       targetFrame.origin.x, targetFrame.origin.y, NX_COPY) ;
  310.    [self unlockFocus] ;
  311.   }
  312.   // reset the targetGlyph ivars
  313.   targetGlyph = tg ;
  314.   tg->flags.isTarget = TRUE ;
  315.   if(tg == rootGlyph) // life is easy if rootGlyph selected!
  316.   { [window flushWindow] ;
  317.     return self ;
  318.   }
  319.   else 
  320.   { // calculate the new target frame (location of
  321.     // new target in my coord system)...
  322.     targetFrame.origin.x = targetFrame.origin.y = 0.0 ;
  323.     targetFrame.size.width = tg->frame.size.width ;
  324.     targetFrame.size.height = tg->frame.size.height ;
  325.     [tg convertToRootGlyph: &targetFrame.origin] ;
  326.     // redraw background under targetFrame, without
  327.     // redrawing the target Glyph itself...
  328.     tg->flags.noDraw = TRUE ;
  329.     [rootGlyph display: &targetFrame] ;
  330.     tg->flags.noDraw = FALSE ;
  331.     [bGView unlockFocus] ;
  332.     // now draw the new target into the fGRnd window
  333.     [fGView lockFocus] ;
  334.     [[fGView window] sizeWindow: targetFrame.size.width
  335.            :targetFrame.size.height] ;
  336.     PSsetgray(1.0) ; // "clear" the old target
  337.     PScompositerect(0.0, 0.0,
  338.        targetFrame.size.width,targetFrame.size.height, NX_COPY) ;
  339.     PSsetgray(0.0) ;
  340.     [tg display: &tg->frame] ;
  341.     [fGView unlockFocus] ;
  342.     return self ;
  343.   }
  344. }
  345.  
  346. - receive: (Glyph *) aGlyt at: (NXPoint *) aPnt  ;
  347. { // add the Glyt to my TTree, with aPnt its origin,
  348.   // given in my coord sys
  349.   Glyph * superG ;
  350.   superG = [rootGlyph hitTest: aPnt] ;
  351.   if(superG) // this should always be true
  352.   { NXRect aRect ;
  353.     aRect.origin = aGlyt->frame.origin = *aPnt ;
  354.     aRect.size = aGlyt->frame.size ;
  355.     [superG convertFromRootGlyph: &(aGlyt->frame.origin)] ;
  356.     [superG plant: aGlyt] ;
  357.     [bGView lockFocus] ;
  358.     [rootGlyph display: &bounds] ; // aRect?
  359.     [bGView unlockFocus] ;
  360.     [self lockFocus] ;
  361.     [self drawSelf: &aRect :1] ;
  362.     [self unlockFocus] ;
  363.     [window flushWindow] ;   
  364.   }
  365.   return self ;
  366. }
  367.  
  368. - rightMouseDown: (NXEvent *) anEvent ;
  369. { [window addToEventMask: NX_LMOUSEDRAGGEDMASK] ;
  370.   [window addToEventMask: NX_RMOUSEDRAGGEDMASK] ;
  371.   [NXApp printf: "D"] ;
  372.   return self ;
  373. }
  374.  
  375.  
  376. - setScrollView: anObject ;
  377. { // this method is invoked curing nib file opening.
  378.   // The opportunity is used to replace the scrollview's
  379.   // docview with ourselves, then do initial setup
  380.   scrollView = anObject ;
  381.   [scrollView setHorizScrollerRequired: YES] ;
  382.   [[scrollView setDocView: self] free] ;
  383.   [self setup] ;
  384.   return self ;
  385. }
  386.  
  387.  
  388. - setup ;
  389. { // perform initial setup of a
  390.   // brand new window
  391.   id bGWindow, fGWindow ;
  392.   NXRect aRect = {{600.0,0.0},{1.0,1.0}} ;
  393.   [window disableCursorRects] ; 
  394.   [window addToEventMask: NX_LMOUSEDRAGGEDMASK] ;
  395.   [window addToEventMask: NX_RMOUSEDRAGGEDMASK] ;
  396.   // create the background windows
  397.   bGWindow = [Window new] ;
  398.   [bGWindow initContent: &bounds style: NX_PLAINSTYLE
  399.        backing: NX_RETAINED buttonMask:0 defer:NO] ;
  400.   bGGState = [bGWindow gState] ; // MUST come AFTER initContent
  401.   bGView = [bGWindow contentView] ;
  402.   fGWindow = [Window new] ;
  403.   [fGWindow initContent: &aRect style: NX_PLAINSTYLE
  404.      backing: NX_RETAINED buttonMask:0 defer: NO] ;
  405.   fGGState = [fGWindow gState] ;
  406.   fGView = [fGWindow contentView] ;
  407.   // setup the rootGlyph
  408.   rootGlyph = [[Glyph alloc] init] ;
  409.   rootGlyph->flags.isRoot = TRUE ;
  410.   [rootGlyph sizeTo: frame.size.width :frame.size.height] ;
  411.   [rootGlyph moveTo: 0.0 :0.0] ;
  412.   // get current visible rectangle
  413.   [[superview superview] getDocVisibleRect: &visibleRect] ;
  414.   // add ourselves to list of glyph views
  415.   [[GlyphView glyphViewList] addObject: self] ;
  416.   // now cook them up for testing.. this will be
  417.   // replaced by a display message to the rootglyph
  418.   // with appropriate lockfocus
  419.   [bGWindow orderFront: 0] ;
  420.   [fGWindow orderFront: 0] ;
  421.   { struct Glyph *aGlyph, *bGlyph, *cGlyph ;
  422.     aGlyph = [[Glyph new] moveTo: 10.0 :15.0] ;
  423.     bGlyph = [[Glyph new] moveTo: 25.0 :25.0] ;
  424.     cGlyph = [[Glyph new] moveTo: 55.0 :55.0] ;
  425.     [rootGlyph plant: aGlyph] ;
  426.     [rootGlyph plant: bGlyph] ;
  427.     [rootGlyph plant: cGlyph] ;
  428.     [rootGlyph iam: "root"] ;
  429.     [aGlyph iam: "a"] ;
  430.     [bGlyph iam: "b"] ;
  431.     [cGlyph iam: "c"] ;
  432.     [bGView lockFocus] ;
  433.     [rootGlyph display: &bounds] ;
  434.     [bGView unlockFocus] ;
  435.   }
  436.  
  437.   return self ;
  438. }  
  439.  
  440. - rightMouseDragged: (NXEvent *) anEvent ;
  441. { [NXApp printf: "R"] ;
  442.   return self ;
  443. }
  444.  
  445.  
  446. - rightMouseUp: (NXEvent *) anEvent ;
  447. { [NXApp printf: "U"] ;
  448.   return self ;
  449. }
  450.  
  451. - windowDidResize: sender ;
  452. { NXRect aRect ;
  453.   float x,y ; 
  454.   [superview getBounds: &aRect] ;
  455.   [self sizeTo: x = MAX(bounds.size.width,aRect.size.width)
  456.               : y = MAX(bounds.size.height,aRect.size.height)] ;
  457.   [[bGView window] sizeWindow:x :y] ; 
  458.   [rootGlyph sizeTo: x :y] ;
  459.   // NOTE: this generates 2 rectangles which will need to
  460.   // be repaired in the bGView....
  461.   // get current visible rectangle
  462.   [[superview superview] getDocVisibleRect: &visibleRect] ;
  463.   return self ;
  464. }
  465.  
  466. - windowWillClose: sender ;
  467. { // our window is about to close; must free up 
  468.   // stuff we created
  469.   [[fGView window] free] ;
  470.   [[bGView window] free] ;
  471.   [[GlyphView glyphViewList] removeObject: self] ;
  472.   return self ;
  473.  
  474.  
  475. @end