ftp.nice.ch/pub/next/developer/resources/classes/RuledViews.s.tar.gz#/RuledViews/BrainRulers/RuledScrollView.m

This is RuledScrollView.m in view mode; [Download] [Up]

// RuledScrollView.m
// RuledScrollView is intended as a general purpose class to make it
// easy to add all kinds of rulers to any view.  This class is an 
// extension of the tiledScrollView class, from "ScrollDoodScroll" 
// (by Jayson Adams, NeXT Developer Support Team)
// This class is intended to be widely useful without needing to
// modify (or understand) its code, so it provides methods to setup 
// the mainView and its rulerViews, rather than hardcoding the setup.
// Kevin Brain (ksbrain@zeus.UWaterloo.ca)

#import <appkit/ClipView.h>
#import <appkit/TextField.h>
#import <appkit/Panel.h>

#import "RuledScrollView.h"

// Macro to print a rect (for debugging)
#define PRINTRECT(string,rect) { \
    printf(" %s x = %4.1f y = %4.1f width = %4.1f height = %4.1f\n",string, \
	NX_X(&(rect)), NX_Y(&(rect)), NX_WIDTH(&(rect)), NX_HEIGHT(&(rect))); \
	}

@implementation RuledScrollView

/* instance methods */

- initFrame:(NXRect *)theFrame
{
    [super initFrame:theFrame];
	rulersOn = YES;
	topRuler = NULL;
	bottomRuler = NULL;
	leftRuler = NULL;
	rightRuler = NULL;
	primaryRulers = TOPBOTTOM;

	return self;
}

- free
{
	if (leftRuler) {
		[leftRuler free];
		[leftRulerClipView free];
		}
	if (rightRuler) {
		[rightRuler free];
		[rightRulerClipView free];
		}
	if (topRuler) {
		[topRuler free];
		[topRulerClipView free];
		}
	if (bottomRuler) {
		[bottomRuler free];
		[bottomRulerClipView free];
		}
	if (bottomLeftStub) {
		[bottomLeftStub free];
		}
	if (topLeftStub) {
		[topLeftStub free];
		}
	if (bottomRightStub) {
		[bottomRightStub free];
		}
	if (topRightStub) {
		[topRightStub free];
		}

    return [super free];
}

- setLeftRuler:anObject
	{
		leftRuler = anObject;
		[self addRulerView:leftRuler toEdge:LEFTEDGE];
	 	return self;
	}

- setRightRuler:anObject
	{
		rightRuler = anObject;
		[self addRulerView:rightRuler toEdge:RIGHTEDGE];
	 	return self;
	}

- setTopRuler:anObject
	{
		topRuler = anObject;
		[self addRulerView:topRuler toEdge:TOPEDGE];
	 	return self;
	}

- setBottomRuler:anObject
	{
		bottomRuler = anObject;
		[self addRulerView:bottomRuler toEdge:BOTTOMEDGE];
	 	return self;
	}

- setBottomLeftStub:anObject
	{
		bottomLeftStub = anObject;
		[self addStubView:anObject toCorner: BOTTOMLEFTCORNER];
	 	return self;
	}

- setTopLeftStub:anObject
	{
		topLeftStub = anObject;
		[self addStubView:anObject toCorner: TOPLEFTCORNER];
	 	return self;
	}

- setBottomRightStub:anObject
	{
		bottomRightStub = anObject;
		[self addStubView:anObject toCorner: BOTTOMRIGHTCORNER];
	 	return self;
	}

- setTopRightStub:anObject
	{
		topRightStub = anObject;
		[self addStubView:anObject toCorner: TOPRIGHTCORNER];
	 	return self;
	}

- addRulerView:(Ruler *)theView toEdge:(int)edge
{
    NXRect	scaleViewRect;
	ClipView *scaleClipView;
    
  /* reset the origin of rulers created in nibs */
	[theView getFrame:&scaleViewRect];
	scaleViewRect.origin.x = 0.0;
	scaleViewRect.origin.y = 0.0;
	[theView setFrame:&scaleViewRect];

	NXSetRect(&scaleViewRect,0,0,0,0);   
  /* make a clipView to hold the scale view passed in
   * (we'll adjust its size & position in tile) */
    scaleClipView = [[ClipView alloc] initFrame:&scaleViewRect];
    [scaleClipView setDocView:theView];
    [self addSubview:scaleClipView];
	
	switch (edge) {
		case LEFTEDGE:
			// actually, I think we leak here if you add the same ruler twice
			// but I'm about to leave on holidays, so I can't be 
			// bothered to change it now.  Next version:-)
			if (leftRuler && (leftRuler != theView)) {	// the view 
				[leftRuler free];
				[leftRulerClipView free];
				}
			leftRuler = theView;
			leftRulerClipView = scaleClipView;
			if([leftRuler respondsTo:@selector(setOrientation:)])
				[leftRuler setOrientation:VERTICAL];	// default is horizontal
			break;
		case RIGHTEDGE:
			if (rightRuler && (rightRuler != theView)) {
				[rightRuler free];
				[rightRulerClipView free];
				}
			rightRuler = theView;
			rightRulerClipView = scaleClipView;
			if([rightRuler respondsTo:@selector(setOrientation:)])
				[rightRuler setOrientation:VERTICAL];	// default is horizontal
			break;
		case TOPEDGE:
			if (topRuler && (topRuler != theView)) {
				[topRuler free];
				[topRulerClipView free];
				}
			topRuler = theView;
			topRulerClipView = scaleClipView;
			break;
		case BOTTOMEDGE:
			if (bottomRuler && (bottomRuler != theView)) {
				[bottomRuler free];
				[bottomRulerClipView free];
				}
			bottomRuler = theView;
			bottomRulerClipView = scaleClipView;
			break;
		}
	rulerVisible[edge] = YES;	// turn this ruler on by default
	return self;
}

- addStubView:(View *)theView toCorner:(int)corner
{
	switch (corner) {
		case BOTTOMLEFTCORNER:
			if (bottomLeftStub && (bottomLeftStub != theView)) 
				[bottomLeftStub free];
			bottomLeftStub = theView;
			break;
		case TOPLEFTCORNER:
			if (topLeftStub && (topLeftStub != theView)) 
				[topLeftStub free];
			topLeftStub = theView;
			break;
		case BOTTOMRIGHTCORNER:
			if (bottomRightStub && (bottomRightStub != theView)) 
				[bottomRightStub free];
			bottomRightStub = theView;
			break;
		case TOPRIGHTCORNER:
			if (topRightStub && (topRightStub != theView)) 
				[topRightStub free];
			topRightStub = theView;
			break;
		}

    [self addSubview:theView];
	return self;
}

- sliceEdge:(int)theEdge forRuler:theRuler inClipView:theClipView
// private method that cuts a slice from the contentView to make room for a ruler
{
    NXRect	contentViewRect, scaleRect, scaleClipRect;
    NXRect	cvBoundsRect;

	// get bounds of contentView so we can reset it after setting frame
	[contentView getBounds:&cvBoundsRect];

	[contentView getFrame:&contentViewRect];
	[theRuler getFrame:&scaleRect];
	if ((theEdge == LEFTEDGE) || (theEdge == RIGHTEDGE))
		NXDivideRect(&contentViewRect, &scaleClipRect, NX_WIDTH(&scaleRect), theEdge);
	else
		NXDivideRect(&contentViewRect, &scaleClipRect, NX_HEIGHT(&scaleRect), theEdge);
	[theClipView setFrame:&scaleClipRect];		// invokes reflectScroll:

	// setting contentView's frame changes contentView bounds then invokes reflectScroll:
	[contentView setFrame:&contentViewRect];

	// reset bounds of contentView mangled by setting frame
	[self scrollClip:contentView to:(NXPoint *)&cvBoundsRect.origin];
		
	return self;
}

- tile
/*
 * tile gets called whenever the scrollView changes size.  Its job is to resize
 * all of the scrollView's "tiled" views (scrollers, contentView and any other
 * views we might want to place in the scrollView's bounds).
 */
{
	NXRect	viewRect, scrollerRect, tempRect, cvBounds, newCVBounds;
	NXRect	stubRect, rulerClipRect, otherRulerRect, docViewRect;
	float	tempFloat=0.0,distanceFromEdge;
    
	[contentView getBounds:&cvBounds];
	[[self docView] getBounds:&docViewRect];

  /* resize and arrange the scrollers and contentView as usual */ 
    [super tile];		// invokes reflectScroll:

	// If the scroller knob is closer to the edge than the width or height
	// of the ruler views, the tile method causes the contentView bounds
	// to be incorrectly changed.  After many frustrating hours of work,
	// I came up with the following code to correct for this...
	
	[contentView getBounds:&newCVBounds];
	NXSetRect(&otherRulerRect,0,0,0,0);
	[[self leftRuler] getFrame:&otherRulerRect];
	if ([self isRulerVisible:LEFTEDGE])
		tempFloat = otherRulerRect.size.width;
	NXSetRect(&otherRulerRect,0,0,0,0);
	[[self rightRuler] getFrame:&otherRulerRect];
	if ([self isRulerVisible:RIGHTEDGE])
		tempFloat += otherRulerRect.size.width;
	distanceFromEdge = docViewRect.size.width - (cvBounds.origin.x+cvBounds.size.width);
	// if entire width of docView was showing, don't adjust
	if (cvBounds.size.width > docViewRect.size.width) 
		distanceFromEdge = tempFloat;
	if ((distanceFromEdge < tempFloat) && (newCVBounds.size.width < docViewRect.size.width))
		newCVBounds.origin.x += (tempFloat-distanceFromEdge);
	
	NXSetRect(&otherRulerRect,0,0,0,0);
	[[self topRuler] getFrame:&otherRulerRect];
	if ([self isRulerVisible:TOPEDGE])
		tempFloat = otherRulerRect.size.height;
	NXSetRect(&otherRulerRect,0,0,0,0);
	[[self bottomRuler] getFrame:&otherRulerRect];
	if ([self isRulerVisible:BOTTOMEDGE])
		tempFloat += otherRulerRect.size.height;
	distanceFromEdge = docViewRect.size.height - (cvBounds.origin.y+cvBounds.size.height);
	// if entire height of docView was showing, don't adjust
	if (cvBounds.size.height > docViewRect.size.height) 
		distanceFromEdge = tempFloat;
	if ((distanceFromEdge < tempFloat) && (newCVBounds.size.height < docViewRect.size.height))
		newCVBounds.origin.y += (tempFloat-distanceFromEdge);
	
	// correct contentView Bounds
	[self scrollClip:contentView to:(NXPoint *)&newCVBounds.origin];

  /* cut a slice from the contentView to make room for each scaleView */
 
	if (primaryRulers == TOPBOTTOM) {
		if(topRuler && rulerVisible[TOPEDGE]) { 
			if ([self isFlipped])
				[self sliceEdge:(int)BOTTOMEDGE forRuler:topRuler inClipView:topRulerClipView];
			else
				[self sliceEdge:(int)TOPEDGE forRuler:topRuler inClipView:topRulerClipView];
			}

		if(bottomRuler && rulerVisible[BOTTOMEDGE]) { 
			if ([self isFlipped])
				[self sliceEdge:(int)TOPEDGE forRuler:bottomRuler inClipView:bottomRulerClipView];
			else
				[self sliceEdge:(int)BOTTOMEDGE forRuler:bottomRuler 
					inClipView:bottomRulerClipView];
			}
		if(leftRuler && rulerVisible[LEFTEDGE])
			[self sliceEdge:(int)LEFTEDGE forRuler:leftRuler inClipView:leftRulerClipView];
		if(rightRuler && rulerVisible[RIGHTEDGE])
			[self sliceEdge:(int)RIGHTEDGE forRuler:rightRuler inClipView:rightRulerClipView];

		// Add stub views (now that edge views are present)
		if(topRuler && rulerVisible[TOPEDGE]) { 
			if (topLeftStub) {
				[leftRulerClipView getFrame:&otherRulerRect];
				[topRulerClipView getFrame:&rulerClipRect];
				NXDivideRect(&rulerClipRect, &stubRect, otherRulerRect.size.width, LEFTEDGE);
				[topRulerClipView setFrame:&rulerClipRect];
				[topLeftStub setFrame:&stubRect];
				}
			if (topRightStub) {
				[rightRulerClipView getFrame:&otherRulerRect];
				[topRulerClipView getFrame:&rulerClipRect];
				NXDivideRect(&rulerClipRect, &stubRect, otherRulerRect.size.width, RIGHTEDGE);
				[topRulerClipView setFrame:&rulerClipRect];
				[topRightStub setFrame:&stubRect];
				}
			}
		if(bottomRuler && rulerVisible[BOTTOMEDGE]) { 
			if (bottomLeftStub) {
				[leftRulerClipView getFrame:&otherRulerRect];
				[bottomRulerClipView getFrame:&rulerClipRect];
				NXDivideRect(&rulerClipRect, &stubRect, otherRulerRect.size.width, LEFTEDGE);
				[bottomRulerClipView setFrame:&rulerClipRect];
				[bottomLeftStub setFrame:&stubRect];
				}
			if (bottomRightStub) {
				[rightRulerClipView getFrame:&otherRulerRect];
				[bottomRulerClipView getFrame:&rulerClipRect];
				NXDivideRect(&rulerClipRect, &stubRect, otherRulerRect.size.width, RIGHTEDGE);
				[bottomRulerClipView setFrame:&rulerClipRect];
				[bottomRightStub setFrame:&stubRect];
				}
			}
		}
	else {
		if(leftRuler && rulerVisible[LEFTEDGE])
			[self sliceEdge:(int)LEFTEDGE forRuler:leftRuler inClipView:leftRulerClipView];
		if(rightRuler && rulerVisible[RIGHTEDGE]) 
			[self sliceEdge:(int)RIGHTEDGE forRuler:rightRuler inClipView:rightRulerClipView];
		if(topRuler && rulerVisible[TOPEDGE]) { 
			if ([self isFlipped])
				[self sliceEdge:(int)BOTTOMEDGE forRuler:topRuler inClipView:topRulerClipView];
			else
				[self sliceEdge:(int)TOPEDGE forRuler:topRuler inClipView:topRulerClipView];
			}
		if(bottomRuler && rulerVisible[BOTTOMEDGE]) { 
			if ([self isFlipped])
				[self sliceEdge:(int)TOPEDGE forRuler:bottomRuler inClipView:bottomRulerClipView];
			else
				[self sliceEdge:(int)BOTTOMEDGE forRuler:bottomRuler 
					inClipView:bottomRulerClipView];
			}
/****************************************************************************/
/*  Code to tile StubViews when (primaryRulers != TOPBOTTOM) still missing  */
/****************************************************************************/
		}

	// The following imbeds a view (normally a button) in the horizontal scroller 
	// if the hScrollerRightEmbeddedView instance variable is connected to some view (in IB)
	// A textfield with Courier-Medium size 10 font will fit into the horizontal scroller.
	if (hScrollerRightEmbeddedView) {
		if ([hScrollerRightEmbeddedView superview] != self) {
			[self addSubview:hScrollerRightEmbeddedView];
			}
			
		[hScrollerRightEmbeddedView getFrame:&tempRect];
		// now make the hScroller smaller and stick the popupList next to it 
		[hScroller getFrame:&scrollerRect];
		// width stored during init
		NXDivideRect(&scrollerRect, &viewRect, tempRect.size.width, RIGHTEDGE);
		// Need to force width back to original button width since the above function
		// will not set viewRect.size.width to tempRect.size.width when the
		// tile method is evoked before the correct size of the mainView is set
		viewRect.size.width = tempRect.size.width;
		[hScroller setFrame:&scrollerRect];
		NXInsetRect(&viewRect, 0, 1.0);
		[hScrollerRightEmbeddedView setFrame:&viewRect];
		}

	// Do the same as above for a possible second view in the horizontal scroller 
	if (hScrollerLeftEmbeddedView) {
		if ([hScrollerLeftEmbeddedView superview] != self) {
			[self addSubview:hScrollerLeftEmbeddedView];
			}
		[hScrollerLeftEmbeddedView getFrame:&tempRect];
		[hScroller getFrame:&scrollerRect];
		NXDivideRect(&scrollerRect, &viewRect, tempRect.size.width, RIGHTEDGE);
		viewRect.size.width = tempRect.size.width;
		[hScroller setFrame:&scrollerRect];
		NXInsetRect(&viewRect, 0, 1.0);
		[hScrollerLeftEmbeddedView setFrame:&viewRect];
		}

	// Do the same as above for a view in the vertical scroller 
	if (vScrollerTopEmbeddedView) {
		if ([vScrollerTopEmbeddedView superview] != self) {
			[self addSubview:vScrollerTopEmbeddedView];
			}
		[vScrollerTopEmbeddedView getFrame:&tempRect];
		[vScroller getFrame:&scrollerRect];
		if ([self isFlipped])
			NXDivideRect(&scrollerRect, &viewRect, tempRect.size.height, BOTTOMEDGE);
		else
			NXDivideRect(&scrollerRect, &viewRect, tempRect.size.height, TOPEDGE);
		viewRect.size.height = tempRect.size.height;
		[vScroller setFrame:&scrollerRect];
		NXInsetRect(&viewRect, 1.0, 0);
		[vScrollerTopEmbeddedView setFrame:&viewRect];
		}

	// Do the same as above for a possible second view in the vertical scroller 
	if (vScrollerBottomEmbeddedView) {
		if ([vScrollerBottomEmbeddedView superview] != self) {
			[self addSubview:vScrollerBottomEmbeddedView];
			}
		[vScrollerBottomEmbeddedView getFrame:&tempRect];
		[vScroller getFrame:&scrollerRect];
		if ([self isFlipped])
			NXDivideRect(&scrollerRect, &viewRect, tempRect.size.height, BOTTOMEDGE);
		else
			NXDivideRect(&scrollerRect, &viewRect, tempRect.size.height, TOPEDGE);
		viewRect.size.height = tempRect.size.height;
		[vScroller setFrame:&scrollerRect];
		NXInsetRect(&viewRect, 1.0, 0);
		[vScrollerBottomEmbeddedView setFrame:&viewRect];
		}

	// save the size of the docView for setSizeIfNeeded
	[[self docView]	getFrame:&oldRect];

	return self;
}

- reflectScroll:cView
// does not affect contentView bounds...
/*
 * We only reflect scroll in the contentView, not the scale views.
 */
{
    return [super reflectScroll:contentView];
}

- scrollClip:aClipView to:(NXPoint *)aPoint
{
    NXPoint	colOrigin,newPoint;
    NXRect	colBounds;
	
	newPoint = *aPoint;
    
  // if it's not the contentView, adjust the point being scrolled to so that
  // it doesn't scroll the contentView except in the direction of the ruler
    [contentView getBounds:&colBounds];
    if (aClipView != contentView)
		if(([aClipView docView] == leftRuler) || ([aClipView docView] == rightRuler))
			newPoint.x = colBounds.origin.x;
		else
			newPoint.y = colBounds.origin.y;
    
  /* turn off drawing (to the screen) */
    [window disableFlushWindow];
    
  /* scroll the contentView to the new origin */
    [contentView rawScroll:&newPoint];
    
  /* compute new origin for the leftRulerView (don't let it scroll horizontally) */
    [leftRulerClipView getBounds:&colBounds];
    colOrigin.x = colBounds.origin.x;
    colOrigin.y = newPoint.y;
    [leftRulerClipView rawScroll:&colOrigin];	// scroll the scale view to that point
    
  /* compute new origin for the rightRulerView (don't let it scroll horizontally) */
    [rightRulerClipView getBounds:&colBounds];
    colOrigin.x = colBounds.origin.x;
    colOrigin.y = newPoint.y;
    [rightRulerClipView rawScroll:&colOrigin];	// scroll the scale view to that point
    
  /* compute new origin for the topRulerView (don't let it scroll vertically) */
    [topRulerClipView getBounds:&colBounds];
    colOrigin.x = newPoint.x ;
    colOrigin.y = colBounds.origin.y;
    [topRulerClipView rawScroll:&colOrigin];	// scroll the scale view to that point
    
  /* compute new origin for the bottomRulerView (don't let it scroll vertically) */
    [bottomRulerClipView getBounds:&colBounds];
    colOrigin.x = newPoint.x ;
    colOrigin.y = colBounds.origin.y;
    [bottomRulerClipView rawScroll:&colOrigin];	// scroll the scale view to that point
    
 /* send results to screen */
    [[window reenableFlushWindow] flushWindow];
    
    return self;
}

- setSizeIfNeeded
{
    NXRect	theFrame;

	[[self docView]	getFrame:&theFrame];
	if ((theFrame.size.width != oldRect.size.width) || 
			(theFrame.size.height != oldRect.size.height))
		[self setSize];
	return self;
}

- setSize
// This method must be invoked whenever the size 
// of the mainView is changed.
{
    NXRect	theFrame;

    [window disableDisplay];
	if (topRuler) 
		[topRuler setSize];
	if (bottomRuler) 
		[bottomRuler setSize];
	if (leftRuler) 
		[leftRuler setSize];
	if (rightRuler) 
		[rightRuler setSize];
	[self resizeSubviews:(const NXSize *)&theFrame.size];
    [[window reenableDisplay] displayIfNeeded];

	return self;
}

- showRuler:(int)whichRuler;
{
	rulerVisible[whichRuler] = YES;
	switch (whichRuler) {
		case TOPEDGE:
			if (topRulerClipView) {
				[self addSubview:topRulerClipView];
				if (leftRulerClipView && rulerVisible[LEFTEDGE]&& topLeftStub)
					[self addSubview:topLeftStub];
				if (rightRulerClipView && rulerVisible[RIGHTEDGE]&& topRightStub)
					[self addSubview:topRightStub];
				}
			break;
		case BOTTOMEDGE:
			if (bottomRulerClipView) {
				[self addSubview:bottomRulerClipView];
				if (leftRulerClipView && rulerVisible[LEFTEDGE]&& bottomLeftStub)
					[self addSubview:bottomLeftStub];
				if (rightRulerClipView && rulerVisible[RIGHTEDGE]&& bottomRightStub)
					[self addSubview:bottomRightStub];
				}
			break;
		case LEFTEDGE:
			if (leftRulerClipView) {
				[self addSubview:leftRulerClipView];
				if (topRulerClipView && rulerVisible[TOPEDGE]&& topLeftStub)
					[self addSubview:topLeftStub];
				if (bottomRulerClipView && rulerVisible[BOTTOMEDGE]&& bottomLeftStub)
					[self addSubview:bottomLeftStub];
				}
			break;
		case RIGHTEDGE:
			if (rightRulerClipView) {
				[self addSubview:leftRulerClipView];
				if (topRulerClipView && rulerVisible[TOPEDGE]&& topRightStub)
					[self addSubview:topRightStub];
				if (bottomRulerClipView && rulerVisible[BOTTOMEDGE]&& bottomRightStub)
					[self addSubview:bottomRightStub];
				}
			break;
		}

	return self;
}

- hideRuler:(int)whichRuler;
// this should be followed promptly with a setSize
{
	rulerVisible[whichRuler] = NO;
	switch (whichRuler) {
		case TOPEDGE:
			if (topRulerClipView)
				[topRulerClipView removeFromSuperview];
			if (topLeftStub)
				[topLeftStub removeFromSuperview];
			if (topRightStub)
				[topRightStub removeFromSuperview];
			break;
		case BOTTOMEDGE:
			if (bottomRulerClipView)
				[bottomRulerClipView removeFromSuperview];
			if (bottomLeftStub)
				[bottomLeftStub removeFromSuperview];
			if (bottomRightStub)
				[bottomRightStub removeFromSuperview];
			break;
		case LEFTEDGE:
			if (leftRulerClipView)
				[leftRulerClipView removeFromSuperview];
			if (topLeftStub)
				[topLeftStub removeFromSuperview];
			if (bottomLeftStub)
				[bottomLeftStub removeFromSuperview];
			break;
		case RIGHTEDGE:
			if (rightRulerClipView)
				[rightRulerClipView removeFromSuperview];
			if (bottomRightStub)
				[bottomRightStub removeFromSuperview];
			if (topRightStub)
				[topRightStub removeFromSuperview];
			break;
		}

	return self;
}

- (BOOL)isRulerVisible:(int)whichRuler 
{ 
	return rulerVisible[whichRuler];
}

- topRuler { return topRuler; }

- bottomRuler { return bottomRuler; }

- leftRuler { return leftRuler; }

- rightRuler { return rightRuler; }

- setPrimaryRulers:(int)orientation
{
	primaryRulers = orientation;
	return self;
}

- bottomLeftStub { return bottomLeftStub; }

- topLeftStub { return topLeftStub; }

- bottomRightStub { return bottomRightStub; }

- topRightStub { return topRightStub; }

- (int)primaryRulers { return primaryRulers; }

- getMinSize:(NXSize *)theSize
{
	NXRect	contentViewRect,ruledScrollViewRect;

	[contentView getFrame:&contentViewRect];
	[self getFrame:&ruledScrollViewRect];
	theSize->width = ruledScrollViewRect.size.width - contentViewRect.size.width+1;
	theSize->height = ruledScrollViewRect.size.height - contentViewRect.size.height+1;

	return	self;
}

/* methods for printing the visible view */ 
/*---------------------------------------*/
- createPrintView
// Creates a view for printing the visible part of the trace
// We do this by adding the rulerViews' ClipViews as subviews to a View object
// (printView) which is allocated once and added as a subview of
// an offscreen window.  When we are done with them, we return the 
// ClipViews to their former position as subviews of the RuledScrollView object
// (self).  The mainView itself is also taken (rather than its ClipView)
// and added to another ClipView object (mainPrintClipView), since it proved
// troublesome to return the mainView (docView) itself when finished with it.

{
	float	leftOffset;
	NXRect	tempRect, clipFrame;
	NXRect	oldMainClipBounds, oldMainFrame;
	
	if (!printWindow) {	// create window & setup mainPrintClipView first time through
						// note that size is fixed at 1120 X 832 
		NXSetRect(&tempRect, (NXCoord) 0, (NXCoord) 0, (NXCoord) 1120, (NXCoord) 832);
		/* create a window for printView */
		printWindow = [[Window alloc] initContent:(const NXRect *)&tempRect
			style:(int)NX_PLAINSTYLE	// no title bar and frame
			backing:(int)NX_BUFFERED
			buttonMask:(int)0	// no controls in title bar and frame 
			defer:(BOOL)NO];
		NXSetRect(&tempRect, (NXCoord) 0, (NXCoord) 0, (NXCoord) 0, (NXCoord) 0);
		printView = [[View alloc] initFrame:&tempRect];
		if ([self isFlipped]) [printView setFlipped:YES];
		mainPrintClipView = [[ClipView alloc] initFrame:&tempRect];
		[[printWindow contentView] addSubview:printView];
		}
				
	/* set pointer to the main View */
	mainView = [self docView];

	/* get translation information (which part of main view is visible) */
	/* we will scroll the main View in its new clipView (a subview of */
	/* PrintView) by sending a scrollPoint: message to it with the point */ 
	/* (main View's clipView's bounds.origin) - (main View's Frame.origin) */
	[[mainView superview] getBounds:&oldMainClipBounds];
	[mainView getFrame:&oldMainFrame];
	mainVisiblePoint.x = oldMainClipBounds.origin.x - oldMainFrame.origin.x;
	mainVisiblePoint.y = oldMainClipBounds.origin.y - oldMainFrame.origin.y;

	// put mainPrintClipView in the same place as it is in ruledScrollView
	[[mainView superview] getFrame:&oldMainClipRect];

	/* get current locations of clipviews */
//	NXSetRect(&oldBottomRect,0,0,0,0);
//	NXSetRect(&oldTopRect,0,0,0,0);
//	NXSetRect(&oldLeftRect,0,0,0,0);
//	NXSetRect(&oldRightRect,0,0,0,0);
	[bottomRulerClipView getFrame:&oldBottomRect];
	[topRulerClipView getFrame:&oldTopRect];
	[leftRulerClipView getFrame:&oldLeftRect];
	[rightRulerClipView getFrame:&oldRightRect];

	/* If the mainView itself is smaller (ie., there is some */
	/* of the tiledScrollView's background showing), reduce the*/
	/* size so that none of the background shows */
//	if (oldMainFrame.size.height < clipFrame.size.height)
//		clipFrame.size.height = signalFrame.size.height;
//	if (oldMainFrame.size.width < clipFrame.size.width)
//		clipFrame.size.width = signalFrame.size.width;
//    [signalClipView setFrame:&clipFrame];
	
//	clipFrame.origin.x = 0;
//	clipFrame.size.width = leftRuleFrame.size.width;
//	[scaleClipView setFrame:&clipFrame];
	
	/* temporarily 'steal' the clipviews holding the rulers and the mainView */
	/* - They are given back in printVisible: */
	[mainPrintClipView setDocView:mainView];
	[printView addSubview:mainPrintClipView];

	if (bottomRuler)
		[printView addSubview:bottomRulerClipView];
	if (topRuler)
		[printView addSubview:topRulerClipView];
	if (leftRuler)
		[printView addSubview:leftRulerClipView];
	if (rightRuler) {
		[printView addSubview:rightRulerClipView];
		}

	if (vScroller) { 
		[vScroller getFrame:&tempRect];
		leftOffset = (tempRect.size.width+4);	// 4 is the width of the borders
		}
	else 
		leftOffset = 0;
	// Now resize the clipviews
	// subtract width of the left vertical scroller since we don't draw it
	tempRect = oldMainClipRect;
	tempRect.origin.x -= leftOffset;	
	[mainPrintClipView setFrame:&tempRect];

	tempRect = oldBottomRect;
	tempRect.origin.x -= leftOffset;
	[bottomRulerClipView setFrame:&tempRect];

	tempRect = oldTopRect;
	tempRect.origin.x -= leftOffset;
	[topRulerClipView setFrame:&tempRect];

	tempRect = oldLeftRect;
	tempRect.origin.x -= leftOffset;
	[leftRulerClipView setFrame:&tempRect];

	tempRect = oldRightRect;
	tempRect.origin.x -= leftOffset;
	[rightRulerClipView setFrame:&tempRect];

	/* scroll the views to the same position as they were in the tiledScrollView */
	[mainView scrollPoint:(const NXPoint *)&mainVisiblePoint];

	/* set the frame of the PrintView to be big enough to show everything */
	clipFrame.origin = oldMainFrame.origin;
	clipFrame.origin.x = 0;
	clipFrame.size = oldMainClipBounds.size;
	clipFrame.size.height += (oldTopRect.size.height + oldBottomRect.size.height);
	clipFrame.size.width += (oldLeftRect.size.width + oldRightRect.size.width);
	[printView setFrame:&clipFrame];

	return printView;
}

- printVisible:sender
{
	if (rulersOn) {
		[window disableFlushWindow];
		[self createPrintView];
		[printView printPSCode:self];
		// mainPrintClipView was setup in docView
		[self setDocView:[mainPrintClipView docView]];
		// return to original views
		[self addSubview:[bottomRuler superview]];
		[self addSubview:[topRuler superview]];
		[self addSubview:[leftRuler superview]];
		[self addSubview:[rightRuler superview]];
		// restore old positions
		[[bottomRuler superview] setFrame:&oldBottomRect];
		[[topRuler superview] setFrame:&oldTopRect];
		[[leftRuler superview] setFrame:&oldLeftRect];
		[[rightRuler superview] setFrame:&oldRightRect];
	
		/* return the view to the origin it was at */
		/* except change the scroll point slightly */
		/* so that the scrollers will update correctly */
		if (rulersOn)
			mainVisiblePoint.y += 1;
		else
			mainVisiblePoint.y -= 1;
		[[self docView] scrollPoint:(const NXPoint *)&mainVisiblePoint];
		[window reenableFlushWindow];
		}
	else
		// remove the message to superview to print the entire main view
		// rather than just the visible portion
		[[[self docView] superview] printPSCode:self];
	return self;
}

- writeToStream:(NXStream *)stream
{
    NXRect	printRect;
	
	if (rulersOn) {
		[window disableFlushWindow];
		[self createPrintView];
		[printView getFrame:&printRect];
		[printView copyPSCodeInside:(const NXRect *)&printRect to:(NXStream *)stream];
		[self setDocView:[mainPrintClipView docView]];
		// return to original views
		[self addSubview:[bottomRuler superview]];
		[self addSubview:[topRuler superview]];
		[self addSubview:[leftRuler superview]];
		[self addSubview:[rightRuler superview]];
		// restore old positions
		[[bottomRuler superview] setFrame:&oldBottomRect];
		[[topRuler superview] setFrame:&oldTopRect];
		[[leftRuler superview] setFrame:&oldLeftRect];
		[[rightRuler superview] setFrame:&oldRightRect];
	
		/* return the view to the origin it was at */
		/* except change the scroll point slightly */
		/* so that the scrollers will update correctly */
		if (rulersOn)
			mainVisiblePoint.y += 1;
		else
			mainVisiblePoint.y -= 1;
		[[self docView] scrollPoint:(const NXPoint *)&mainVisiblePoint];
		[window reenableFlushWindow];
		}
	else {
		[[[self docView] superview] getBounds:&printRect];
		[[[self docView] superview] copyPSCodeInside:(const NXRect *)&printRect 
			to:(NXStream *)stream];
		}
	return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.