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.