This is SyncScrollView.m in view mode; [Download] [Up]
#import "draw.h" @implementation SyncScrollView /* * This subclass of ScrollView is extremely useful for programmers * who want some View to scroll along with a main docView. A good * example is a spreadsheet that wants its column and row headings * to scroll along with the cells in the spreadsheet itself. * It is actually quite simple. We simply override tile to place * two ClipViews with our views (rulers in this case) in them into * the view hierarchy, then override scrollClip:to: to update their * drawing origins when the docView is scrolled. We also override * reflectScroll: since we don't want that to apply to our little * ruler views, only to the main docView. */ - setRulerClass:factoryId { if ([factoryId conformsTo:@protocol(Ruler)]) rulerClass = factoryId; return self; } - setRulerWidths:(NXCoord)horizontal :(NXCoord)vertical { horizontalRulerWidth = horizontal; verticalRulerWidth = vertical; return self; } - (BOOL)bothRulersAreVisible { return verticalRulerIsVisible && horizontalRulerIsVisible; } - (BOOL)eitherRulerIsVisible { return verticalRulerIsVisible || horizontalRulerIsVisible; } - (BOOL)verticalRulerIsVisible { return verticalRulerIsVisible; } - (BOOL)horizontalRulerIsVisible { return horizontalRulerIsVisible; } - setRulerOrigin:(RulerOrigin)origin { RulerOrigin oldRulerOrigin = rulerOrigin; rulerOrigin = origin; switch (origin) { case LowerRight: [[hClipRuler docView] setFlipped:YES]; break; case UpperRight: [[hClipRuler docView] setFlipped:YES]; case UpperLeft: [[vClipRuler docView] setFlipped:YES]; case LowerLeft: break; default: rulerOrigin = oldRulerOrigin; break; } return self; } - makeRulers /* * This makes the rulers. * We do this lazily in case the user never asks for the rulers. */ { View <Ruler> *ruler; NXRect aRect, bRect; if (!rulerClass || (!horizontalRulerWidth && !verticalRulerWidth)) return nil; if (horizontalRulerWidth) { [[contentView docView] getFrame:&aRect]; NXDivideRect(&aRect, &bRect, horizontalRulerWidth, NX_YMIN); hClipRuler = [[ClipView allocFromZone:[self zone]] init]; ruler = [[rulerClass allocFromZone:[self zone]] initFrame:&bRect]; [hClipRuler setDocView:ruler]; } if (verticalRulerWidth) { [[contentView docView] getFrame:&aRect]; NXDivideRect(&aRect, &bRect, verticalRulerWidth, NX_XMIN); vClipRuler = [[ClipView allocFromZone:[self zone]] init]; ruler = [[rulerClass allocFromZone:[self zone]] initFrame:&bRect]; [vClipRuler setDocView:ruler]; } [self setRulerOrigin:rulerOrigin]; rulersMade = 1; return self; } - updateRulers:(const NXRect *)rect { if (!rect) { if (verticalRulerIsVisible) { [[vClipRuler docView] hidePosition]; } if (horizontalRulerIsVisible) { [[hClipRuler docView] hidePosition]; } } else { if (verticalRulerIsVisible) { [[vClipRuler docView] showPosition:rect->origin.y :rect->origin.y + rect->size.height]; } if (horizontalRulerIsVisible) { [[hClipRuler docView] showPosition:rect->origin.x :rect->origin.x + rect->size.width]; } } return self; } - updateRuler { NXRect aRect, bRect; if (horizontalRulerIsVisible) { [[contentView docView] getFrame:&aRect]; NXDivideRect(&aRect, &bRect, horizontalRulerWidth, NX_YMIN); bRect.size.width += verticalRulerWidth; [[hClipRuler docView] setFrame:&bRect]; [hClipRuler display]; } if (verticalRulerIsVisible) { [[contentView docView] getFrame:&aRect]; NXDivideRect(&aRect, &bRect, verticalRulerWidth, NX_XMIN); [[vClipRuler docView] setFrame:&bRect]; [vClipRuler display]; } return self; } - (BOOL)showRuler:(BOOL)showIt isHorizontal:(BOOL)isHorizontal /* * Adds or removes a ruler from the view hierarchy. * Returns whether or not it succeeded in doing so. */ { ClipView *ruler; BOOL isVisible; NXRect cRect, rRect; isVisible = isHorizontal ? horizontalRulerIsVisible : verticalRulerIsVisible; if ((showIt && isVisible) || (!showIt && !isVisible)) return NO; if (showIt && !rulersMade && ![self makeRulers]) return NO; ruler = isHorizontal ? hClipRuler : vClipRuler; if (!showIt && isVisible) { [ruler removeFromSuperview]; if (isHorizontal) { horizontalRulerIsVisible = NO; } else { verticalRulerIsVisible = NO; } } else if (showIt && !isVisible && ruler) { [self addSubview:ruler]; [window disableDisplay]; [contentView getBounds:&cRect]; [hClipRuler getBounds:&rRect]; [hClipRuler setDrawOrigin:cRect.origin.x :rRect.origin.y]; [vClipRuler getBounds:&rRect]; [vClipRuler setDrawOrigin:rRect.origin.x :cRect.origin.y]; [window reenableDisplay]; if (isHorizontal) { horizontalRulerIsVisible = YES; } else { verticalRulerIsVisible = YES; } } return YES; } - adjustSizes { id windelegate; NXRect winFrame; windelegate = [window delegate]; if ([windelegate respondsTo:@selector(windowWillResize:toSize:)]) { [window getFrame:&winFrame]; [windelegate windowWillResize:window toSize:&winFrame.size]; [window placeWindow:&winFrame]; } [self resizeSubviews:(NXSize *)nil]; return self; } - showHorizontalRuler:(BOOL)flag { if ([self showRuler:flag isHorizontal:YES]) [self adjustSizes]; return self; } - showVerticalRuler:(BOOL)flag { if ([self showRuler:flag isHorizontal:NO]) [self adjustSizes]; return self; } - showHideRulers:sender /* * If both rulers are visible, they are both hidden. * Otherwise, both rulers are made visible. */ { BOOL resize = NO; if (verticalRulerIsVisible && horizontalRulerIsVisible) { resize = [self showRuler:NO isHorizontal:YES]; resize = [self showRuler:NO isHorizontal:NO] || resize; } else { if (!horizontalRulerIsVisible) resize = [self showRuler:YES isHorizontal:YES]; if (!verticalRulerIsVisible) resize = [self showRuler:YES isHorizontal:NO] || resize; } if (resize) [self adjustSizes]; return self; } /* ScrollView-specific stuff */ - free { if (!horizontalRulerIsVisible) [hClipRuler free]; if (!verticalRulerIsVisible) [vClipRuler free]; return [super free]; } - reflectScroll:cView /* * We only reflect scroll in the contentView, not the rulers. */ { if (cView == hClipRuler || cView == vClipRuler) return self; return [super reflectScroll:cView]; } - tile /* * Here is where we lay out the subviews of the ScrollView. * Note the use of NXDivideRect() to "slice off" a section of * a rectangle. This is useful since the two scrollers each * result in slicing a section off the contentView of the * ScrollView. */ { NXRect aRect, bRect, cRect; [super tile]; if (horizontalRulerIsVisible || verticalRulerIsVisible) { [contentView getFrame:&aRect]; [[self docView] getFrame:&cRect]; if (horizontalRulerIsVisible && hClipRuler) { NXDivideRect(&aRect, &bRect, horizontalRulerWidth, NX_YMIN); [hClipRuler setFrame:&bRect]; [[hClipRuler docView] sizeTo:cRect.size.width+verticalRulerWidth :bRect.size.height]; } if (verticalRulerIsVisible && vClipRuler) { NXDivideRect(&aRect, &bRect, verticalRulerWidth, NX_XMIN); [vClipRuler setFrame:&bRect]; [[vClipRuler docView] sizeTo:bRect.size.width :cRect.size.height]; } [contentView setFrame:&aRect]; } return self; } - scrollClip:(ClipView *)aClipView to:(const NXPoint *)aPoint /* * This is sent to us instead of rawScroll:. * We scroll the two rulers, then the clipView itself. */ { id fr; NXRect rRect; if (horizontalRulerIsVisible && hClipRuler) { [hClipRuler getBounds:&rRect]; rRect.origin.x = aPoint->x; [hClipRuler rawScroll:&(rRect.origin)]; } if (verticalRulerIsVisible && vClipRuler) { [vClipRuler getBounds:&rRect]; rRect.origin.y = aPoint->y; [vClipRuler rawScroll:&(rRect.origin)]; } [aClipView rawScroll:aPoint]; fr = [window firstResponder]; if ([fr respondsTo:@selector(isRulerVisible)] && [fr isRulerVisible]) [fr updateRuler]; // keeps Text ruler up-to-date return self; } - descendantFrameChanged:sender /* * Any time the docView is resized, this method is * called to update the size of the rulers to be equal to * the size of the docView. */ { NXRect aRect, bRect, cRect; [super descendantFrameChanged:sender]; if (horizontalRulerIsVisible || verticalRulerIsVisible) { [contentView getFrame:&aRect]; [[self docView] getFrame:&cRect]; if (horizontalRulerIsVisible && hClipRuler) { NXDivideRect(&aRect, &bRect, horizontalRulerWidth, NX_YMIN); [[hClipRuler docView] sizeTo:cRect.size.width+verticalRulerWidth :bRect.size.height]; } if (verticalRulerIsVisible && vClipRuler) { NXDivideRect(&aRect, &bRect, verticalRulerWidth, NX_XMIN); [[vClipRuler docView] sizeTo:bRect.size.width :cRect.size.height]; } } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.