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 ;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.