This is GlyphView.m in view mode; [Download] [Up]
#pragma .h #import <appkit/View.h> #pragma .h #import "Glyph.h" #import "GlyphView.h" #import <appkit/Application.h> #import <appkit/Window.h> #import <appkit/NXCursor.h> #import <dpsclient/psops.h> #import <dpsclient/wraps.h> #import <c.h> #import "/local/grd/CB/MenuManager.h" #import <appkit/ScrollView.h> #import <appkit/publicWraps.h> #import <objc/List.h> static id glyphViewList ; @implementation GlyphView: View { id fGView, bGView, scrollView ; Glyph *rootGlyph, *targetGlyph, *superGlyph ; int fGGState, bGGState ; NXRect targetFrame ; // targetGlyph's frame in my coord sys NXRect clipRect ; // clipping rectangle around targetGlyph NXRect visibleRect ; // visible portion of rootGlyph NXPoint mouseOffset ; // distance from mouse hit to target's origin BOOL rootIsTarget, isAWindow ; } + glyphViewList ; { // return the list of glyph views if(glyphViewList == nil) glyphViewList = [[List alloc] init] ; return glyphViewList ; } -blowUp ; // Provide visual feedback that the target glyph is // dying, then kill it. { NXRect aRect ; float dx, dy ; id aWin ; int i , knt = 20 ; // shrink window to nothing aWin = [fGView window] ; [aWin orderFront: self] ; // may be offscreen [aWin getFrame: &aRect] ; dx = aRect.size.width / (float) knt ; dy = aRect.size.height / (float) knt ; for(i = 0 ; i < knt ; i++) { [aWin sizeWindow: aRect.size.width :aRect.size.height] ; NXPing() ; aRect.size.width -= dx ; aRect.size.height -= dy ; } [aWin orderOut: self] ; [targetGlyph free] ; // actually, doesn't know how to free yet! targetGlyph = nil ; return self ; } -clipRect: (NXRect *) aRect toGlyph: (Glyph *) aGlyph ; // set aRect to the clipping rectangle imposed by aGlyph. // This involves calculating intersection rectangles // back to the rootGlyph. Just calculates the clipping // rectangle; does not actually set it. { if(aGlyph->flags.isRoot) { aRect->origin.x = aGlyph->frame.origin.x ; aRect->origin.y = aGlyph->frame.origin.y ; aRect->size.width = aGlyph->frame.size.width ; aRect->size.height = aGlyph->frame.size.height ; return self ; } else { NXRect bRect = {{0.0,0.0},{0.0,0.0}} ; [self clipRect: aRect toGlyph: aGlyph->ancestor] ; bRect.size.height = aGlyph->frame.size.height ; bRect.size.width = aGlyph->frame.size.width ; [aGlyph convertToRootGlyph: &bRect.origin] ; NXIntersectionRect(&bRect,aRect) ; } return self ; } -convertToScreen: (NXPoint *) aPnt ; // convert aPnt from my coord sys to screen coord sys { [self convertPoint: aPnt toView: nil] ; [window convertBaseToScreen: aPnt] ; return self ; } -drawSelf:(const NXRect *)rects :(int)rectCount ; { // drawSelf: does the displaying when my window // is first opened, when its resized, when its // scrolled, or when its unminiaturized. // composite background PScomposite(rects->origin.x, rects->origin.y, rects->size.width, rects->size.height, bGGState, rects->origin.x, rects->origin.y, NX_COPY) ; // and, if necessary, the foreground if(targetGlyph && NXIntersectsRect(rects, &targetFrame)) { NXRect unionRect ; float x,y ; unionRect = targetFrame ; NXUnionRect(rects,&unionRect) ; x = unionRect.origin.x - targetFrame.origin.x ; y = unionRect.origin.y - targetFrame.origin.y ; PScomposite(x, y, unionRect.size.width, unionRect.size.height, fGGState, unionRect.origin.x, unionRect.origin.y, NX_SOVER) ; } return self ; } - handOff: (NXPoint *) aPnt ; { // Attempts to handOff: the current targetGlyph to another // glyphView, if aPnt (the current mouse point in screen coords) // is within that view. Deletes the target if it fails. In // either case, sets the targetGlyph to nil. NXRect visRect ; NXPoint thePnt ; int found, gWinNum, fGWinNum, targetWin, i, knt ; id theList, theView ; NXConvertWinNumToGlobal([[fGView window] windowNum],&fGWinNum) ; [[fGView window] orderOut: self] ; // don't find the fG window PSfindwindow(aPnt->x,aPnt->y,NX_ABOVE,0, &thePnt.x,&thePnt.y,&gWinNum,&found) ; if(!found) return nil ; NXConvertGlobalToWinNum(gWinNum, &targetWin) ; theList = [GlyphView glyphViewList] ; knt = [theList count] ; for(i = 0 ; i < knt ; i++) { if(( [[(theView = [theList objectAt: i]) window] windowNum]) == targetWin) { // we've got a candidate... NXPoint viewPnt ; viewPnt = *aPnt ; [[theView window] convertScreenToBase: &viewPnt] ; [theView convertPoint: &viewPnt fromView: nil] ; [[[theView superview] superview] getDocVisibleRect: &visRect] ; if(NXPointInRect(&viewPnt,&visRect)) // give it away... { viewPnt.x -= mouseOffset.x ; viewPnt.y -= mouseOffset.y ; [theView receive: targetGlyph at: &viewPnt] ; targetGlyph = nil ; return self ; } else [self blowUp] ; break ; } } return nil ; } - mouseDown: (NXEvent *) anEvent ; { Glyph *tg ; BOOL newTarget = NO ; [self lockFocus] ; [self convertPoint: &anEvent->location fromView: nil] ; tg = [rootGlyph hitTest: &anEvent->location] ; if(tg != targetGlyph) // new target { [self newTarget: tg] ; newTarget = YES ; } if(tg == rootGlyph) { rootIsTarget = YES ; return self ; } rootIsTarget = NO ; // set the clipping rectangle PSgsave() ; superGlyph = tg->ancestor ; [self clipRect: &clipRect toGlyph: superGlyph] ; NXRectClip(&clipRect) ; // remove target glyph from TTree [targetGlyph unlink] ; // calculate new mouse offset mouseOffset.x = anEvent->location.x - targetFrame.origin.x ; mouseOffset.y = anEvent->location.y - targetFrame.origin.y ; isAWindow = false ; // initially, glyph must be in our view // finally, highlight the new target if(newTarget) { [self lockFocus] ; PSsetgray(0.666) ; PScompositerect(targetFrame.origin.x,targetFrame.origin.y, targetFrame.size.width,targetFrame.size.height, NX_PLUS) ; [self unlockFocus] ; [window flushWindow] ; } return self ; } - mouseDragged: (NXEvent *) anEvent ; { Glyph *aGlyph ; if(rootIsTarget) return self ; // can't drag the root glyph [self convertPoint: &anEvent->location fromView: nil] ; // see if we've dragged out of our window if(!isAWindow) // i.e. if glyph is in our view... { // erase our old image PScomposite(targetFrame.origin.x, targetFrame.origin.y, targetFrame.size.width, targetFrame.size.height, bGGState, targetFrame.origin.x, targetFrame.origin.y,NX_COPY) ; if(!NXPointInRect(&anEvent->location,&visibleRect)) // just dragged out of view... { NXPoint winPnt ; // turn into a "travelling window" winPnt.x = targetFrame.origin.x ; winPnt.y = targetFrame.origin.y ; [self convertToScreen: &winPnt] ; [[fGView window] moveTo: winPnt.x :winPnt.y] ; [[fGView window] orderFront: self] ; isAWindow = YES ; } } else // glyph dragged out of our view, so is a "travelling window" { if(NXPointInRect(&anEvent->location,&visibleRect)) // dragged back in? { [[fGView window] orderOut: self] ; isAWindow = NO ; } else // glyph was, and still is, a "travelling window" { NXPoint winPnt ; // move the "travelling window" winPnt.x = targetFrame.origin.x ; winPnt.y = targetFrame.origin.y ; [self convertPoint: &winPnt toView: nil] ; [window convertBaseToScreen:&winPnt] ; [[fGView window] moveTo: winPnt.x :winPnt.y] ; } } // update the target's location targetFrame.origin.x = anEvent->location.x - mouseOffset.x ; targetFrame.origin.y = anEvent->location.y - mouseOffset.y ; if(!isAWindow) // if not a "travelling window".... { // see if clip rectangle must change aGlyph = [rootGlyph hitTest: &anEvent->location] ; if(aGlyph && (aGlyph != superGlyph)) { // must reset the clip rectangle superGlyph = aGlyph ; PSgrestore() ; PSgsave() ; [self clipRect: &clipRect toGlyph: superGlyph] ; NXRectClip(&clipRect) ; } // draw our new image PScomposite(0.0, 0.0,targetFrame.size.width,targetFrame.size.height, fGGState,targetFrame.origin.x, targetFrame.origin.y,NX_PLUS) ; // finally, highlight the new target [self lockFocus] ; PSsetgray(0.666) ; PScompositerect(targetFrame.origin.x,targetFrame.origin.y, targetFrame.size.width,targetFrame.size.height, NX_PLUS) ; [self unlockFocus] ; [window flushWindow] ; } return self ; } - mouseUp: (NXEvent *) anEvent ; { if(rootIsTarget) return self ; PSgrestore() ; [self unlockFocus] ; if(isAWindow) { [[self window] convertBaseToScreen: &anEvent->location] ; [self handOff: &anEvent->location] ; // hand it to another view, or the void! return self ; } [self convertPoint: &anEvent->location fromView: nil] ; // relink the target // Is the next hitTest necessary? superGlyph = [rootGlyph hitTest: &anEvent->location] ; [superGlyph convertFromRootGlyph: &anEvent->location] ; [targetGlyph moveTo: anEvent->location.x - mouseOffset.x : anEvent->location.y - mouseOffset.y ] ; [superGlyph plant: targetGlyph] ; return self ; } - newTarget: (Glyph *) tg ; { // This is called whenever a new target is selected. // (A smarter version // would know if the fg was "growing" or "shrinking".) // To repair the background, first composite the fGrnd // into the background... [bGView lockFocus] ; if(targetGlyph != nil) { // composite visible portion of old target into bGrnd NXIntersectionRect(&clipRect,&targetFrame) ; PScomposite(0.0,0.0, targetFrame.size.width,targetFrame.size.height,fGGState, targetFrame.origin.x, targetFrame.origin.y, NX_PLUS) ; targetGlyph->flags.isTarget = FALSE ; // repair the area in offscreen window [self lockFocus] ; PScomposite(targetFrame.origin.x,targetFrame.origin.y, targetFrame.size.width,targetFrame.size.height,bGGState, targetFrame.origin.x, targetFrame.origin.y, NX_COPY) ; [self unlockFocus] ; } // reset the targetGlyph ivars targetGlyph = tg ; tg->flags.isTarget = TRUE ; if(tg == rootGlyph) // life is easy if rootGlyph selected! { [window flushWindow] ; return self ; } else { // calculate the new target frame (location of // new target in my coord system)... targetFrame.origin.x = targetFrame.origin.y = 0.0 ; targetFrame.size.width = tg->frame.size.width ; targetFrame.size.height = tg->frame.size.height ; [tg convertToRootGlyph: &targetFrame.origin] ; // redraw background under targetFrame, without // redrawing the target Glyph itself... tg->flags.noDraw = TRUE ; [rootGlyph display: &targetFrame] ; tg->flags.noDraw = FALSE ; [bGView unlockFocus] ; // now draw the new target into the fGRnd window [fGView lockFocus] ; [[fGView window] sizeWindow: targetFrame.size.width :targetFrame.size.height] ; PSsetgray(1.0) ; // "clear" the old target PScompositerect(0.0, 0.0, targetFrame.size.width,targetFrame.size.height, NX_COPY) ; PSsetgray(0.0) ; [tg display: &tg->frame] ; [fGView unlockFocus] ; return self ; } } - receive: (Glyph *) aGlyt at: (NXPoint *) aPnt ; { // add the Glyt to my TTree, with aPnt its origin, // given in my coord sys Glyph * superG ; superG = [rootGlyph hitTest: aPnt] ; if(superG) // this should always be true { NXRect aRect ; aRect.origin = aGlyt->frame.origin = *aPnt ; aRect.size = aGlyt->frame.size ; [superG convertFromRootGlyph: &(aGlyt->frame.origin)] ; [superG plant: aGlyt] ; [bGView lockFocus] ; [rootGlyph display: &bounds] ; // aRect? [bGView unlockFocus] ; [self lockFocus] ; [self drawSelf: &aRect :1] ; [self unlockFocus] ; [window flushWindow] ; } return self ; } - rightMouseDown: (NXEvent *) anEvent ; { [window addToEventMask: NX_LMOUSEDRAGGEDMASK] ; [window addToEventMask: NX_RMOUSEDRAGGEDMASK] ; [NXApp printf: "D"] ; return self ; } - setScrollView: anObject ; { // this method is invoked curing nib file opening. // The opportunity is used to replace the scrollview's // docview with ourselves, then do initial setup scrollView = anObject ; [scrollView setHorizScrollerRequired: YES] ; [[scrollView setDocView: self] free] ; [self setup] ; return self ; } - setup ; { // perform initial setup of a // brand new window id bGWindow, fGWindow ; NXRect aRect = {{600.0,0.0},{1.0,1.0}} ; [window disableCursorRects] ; [window addToEventMask: NX_LMOUSEDRAGGEDMASK] ; [window addToEventMask: NX_RMOUSEDRAGGEDMASK] ; // create the background windows bGWindow = [Window new] ; [bGWindow initContent: &bounds style: NX_PLAINSTYLE backing: NX_RETAINED buttonMask:0 defer:NO] ; bGGState = [bGWindow gState] ; // MUST come AFTER initContent bGView = [bGWindow contentView] ; fGWindow = [Window new] ; [fGWindow initContent: &aRect style: NX_PLAINSTYLE backing: NX_RETAINED buttonMask:0 defer: NO] ; fGGState = [fGWindow gState] ; fGView = [fGWindow contentView] ; // setup the rootGlyph rootGlyph = [[Glyph alloc] init] ; rootGlyph->flags.isRoot = TRUE ; [rootGlyph sizeTo: frame.size.width :frame.size.height] ; [rootGlyph moveTo: 0.0 :0.0] ; // get current visible rectangle [[superview superview] getDocVisibleRect: &visibleRect] ; // add ourselves to list of glyph views [[GlyphView glyphViewList] addObject: self] ; // now cook them up for testing.. this will be // replaced by a display message to the rootglyph // with appropriate lockfocus [bGWindow orderFront: 0] ; [fGWindow orderFront: 0] ; { struct Glyph *aGlyph, *bGlyph, *cGlyph ; aGlyph = [[Glyph new] moveTo: 10.0 :15.0] ; bGlyph = [[Glyph new] moveTo: 25.0 :25.0] ; cGlyph = [[Glyph new] moveTo: 55.0 :55.0] ; [rootGlyph plant: aGlyph] ; [rootGlyph plant: bGlyph] ; [rootGlyph plant: cGlyph] ; [rootGlyph iam: "root"] ; [aGlyph iam: "a"] ; [bGlyph iam: "b"] ; [cGlyph iam: "c"] ; [bGView lockFocus] ; [rootGlyph display: &bounds] ; [bGView unlockFocus] ; } return self ; } - rightMouseDragged: (NXEvent *) anEvent ; { [NXApp printf: "R"] ; return self ; } - rightMouseUp: (NXEvent *) anEvent ; { [NXApp printf: "U"] ; return self ; } - windowDidResize: sender ; { NXRect aRect ; float x,y ; [superview getBounds: &aRect] ; [self sizeTo: x = MAX(bounds.size.width,aRect.size.width) : y = MAX(bounds.size.height,aRect.size.height)] ; [[bGView window] sizeWindow:x :y] ; [rootGlyph sizeTo: x :y] ; // NOTE: this generates 2 rectangles which will need to // be repaired in the bGView.... // get current visible rectangle [[superview superview] getDocVisibleRect: &visibleRect] ; return self ; } - windowWillClose: sender ; { // our window is about to close; must free up // stuff we created [[fGView window] free] ; [[bGView window] free] ; [[GlyphView glyphViewList] removeObject: self] ; return self ; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.