
// DBTableViewPrinter -- An object for printing DBTableViews.
//
// By Eric T. Seymour, NeXT Computer, Inc.
//
// This object formats and prints DBTableViews.  Basically, the drawself:
// method draws the table (in brute force fashion).  It also contains methods
// for setting options.
//
// You may freely copy, distribute, and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or  implied, as to its
// fitness for any particular use.
//
// This file looks best when using tabstops of 3.


#import <dbkit/dbkit.h>
#import <appkit/appkit.h>
#import "DBTableViewPrinter.h"
#import "TablePrintPanel.h"

#define	CURRENT_CLASS_VERSION	1

@interface DBTableViewPrinter(Private)
- refreshLayout;								// Page Layout stuff
@end

@implementation DBTableViewPrinter

+ initialize 
{
	// Set version for read/write backward compatibility
	if ( self == [DBTableViewPrinter class] )
		[DBTableViewPrinter setVersion:CURRENT_CLASS_VERSION];

	return self;
}

- initFrame:(const NXRect *)frameRect
{
	// initialize the frame
	[super initFrame:frameRect];
	[self setFlipped:YES];

	// Initialize some flags and variables
	tableView = nil;
	rowHeight = 0.0;
	pageNumSepChar = '-';
	isPrinting = NO;
	isPageNumbersEnabled = NO;
	isRowNumbersEnabled = NO;
	isSelectedRowsOnly = NO;
	isForceDrawColor = NO;
	startingPageNumber = 1;
	startingRowNumber = 1;
	isGridLinesOn = YES;
	strcpy(rowNumberHeaderTitle,"#");
	gridColor = NX_COLORBLACK;
	backgroundColor = NX_COLORWHITE;
	rowNumbersBackColor = NXConvertGrayToColor(0.17);

	// Create Cell with which to draw titles
	headerTextCell = [[TextFieldCell alloc] initTextCell:""];
	[headerTextCell setFont:
		[Font boldSystemFontOfSize:12.0 matrix:NX_FLIPPEDMATRIX]];
	[headerTextCell setWrap:NO];
	[headerTextCell setAlignment:NX_CENTERED];
	[headerTextCell setTextColor:NX_COLORWHITE];
	[headerTextCell setTextGray:NX_WHITE];
	[headerTextCell setBackgroundGray:0.17];
	[headerTextCell calcCellSize:&headerTextCellSize];

	// Create Cell with which to draw row number headers
	rowNumberHeaderCell = [[TextFieldCell alloc] initTextCell:""];
	[rowNumberHeaderCell setFont:
		[Font boldSystemFontOfSize:12.0 matrix:NX_FLIPPEDMATRIX]];
	[rowNumberHeaderCell setWrap:NO];
	[rowNumberHeaderCell setAlignment:NX_CENTERED];
	[rowNumberHeaderCell setTextColor:NX_COLORWHITE];
	[rowNumberHeaderCell setTextGray:NX_WHITE];
	[rowNumberHeaderCell setBackgroundGray:0.17];

	// Create Cell with which to draw row numbers
	rowNumberCell = [[TextFieldCell alloc] initTextCell:""];
	[rowNumberCell setFont:
		[Font boldSystemFontOfSize:12.0 matrix:NX_FLIPPEDMATRIX]];
	[rowNumberCell setWrap:NO];
	[rowNumberCell setAlignment:NX_RIGHTALIGNED];
	[rowNumberCell setTextColor:NX_COLORWHITE];
	[rowNumberCell setTextGray:NX_WHITE];
	[rowNumberCell setBackgroundGray:-1.0];	// Make background 100% Alpha

	// Create Cell with which to draw page number
	pageNumberCell = [[TextFieldCell alloc] initTextCell:""];
	[pageNumberCell setWrap:NO];
	[pageNumberCell setTextColor:NX_COLORBLACK];
	[pageNumberCell setTextGray:NX_BLACK];
	[pageNumberCell calcCellSize:&pageNumberCellSize];
	[pageNumberCell setBackgroundGray:-1.0];

	// set my page frame
	if ( frameRect )
	{
		pageFrame.origin.x = frameRect->origin.x;
		pageFrame.origin.y = frameRect->origin.y;
		pageFrame.size.width = frameRect->size.width;
		pageFrame.size.height = frameRect->size.height;
	}
	else 
	{
		pageFrame.origin.x = pageFrame.origin.y = 0.0;
		pageFrame.size.width = pageFrame.size.height = 20.0;
	}

	return self;
}

// Toggles and methods for color drawing
- setForceDrawColor:(BOOL)toggle
{
	isForceDrawColor = toggle;
	return self;
}
- (BOOL)isForceDrawColor{return isForceDrawColor;}

- (BOOL)shouldDrawColor
{
	if ( isForceDrawColor ) return YES;
	else return [super shouldDrawColor];
}

- free
{
	// Free the Cells I was using
	[headerTextCell free];
	[rowNumberHeaderCell free];
	[rowNumberCell free];
	[pageNumberCell free];
	return [super free];
}

- setPageNumbersEnabled:(BOOL)toggle
{
	isPageNumbersEnabled = toggle;
	if ( isPrinting ) [self refreshLayout];
	return self;
}
- (BOOL)isPageNumbersEnabled{return isPageNumbersEnabled;}

- setStartingPageNumber:(int)num
{
	startingPageNumber = num;
	return self;
}
- (int)startingPageNumber{return startingPageNumber;}

- setPageNumberSeparator:(char)ch
{
	if ( NXIsPrint(ch) ) pageNumSepChar = ch;
	return self;
}
- (char)pageNumberSeparator{return pageNumSepChar;}

- setRowNumbersEnabled:(BOOL)toggle
{
	isRowNumbersEnabled = toggle;
	if ( isPrinting ) [self refreshLayout];
	return self;
}
- (BOOL)isRowNumbersEnabled{return isRowNumbersEnabled;}

- setStartingRowNumber:(int)num
{
	startingRowNumber = num;
	if ( isPrinting ) [self refreshLayout];
	return self;
}
- (int)startingRowNumber{return startingRowNumber;}

- setRowNumbersTitle:(const char *)title
{
	if ( title == NULL ) return nil;
	memset(rowNumberHeaderTitle,0,51);
	strncpy(rowNumberHeaderTitle,title,50);
	if ( isPrinting ) [self refreshLayout];
	return self;
}
- (const char *)rowNumbersTitle{return rowNumberHeaderTitle;}

- setGridLinesOn:(BOOL)toggle
{
	isGridLinesOn = toggle;
	return self;
}
- (BOOL)isGridLinesOn{return isGridLinesOn;}

- setSelectedRowsOnly:(BOOL)toggle
{
	isSelectedRowsOnly = toggle;
	if ( isPrinting ) [self refreshLayout];
	return self;
}
- (BOOL)isSelectedRowsOnly{return isSelectedRowsOnly;}

// Set Headers Bezeled Toggle

- setColumnHeadersBezeled:(BOOL)toggle
{
	[headerTextCell setBezeled:toggle];
	[rowNumberHeaderCell setBezeled:toggle];
	[headerTextCell calcCellSize:&headerTextCellSize];
	return self;
}
- (BOOL)isColumnHeadersBezeled{return [headerTextCell isBezeled];}

// Colors

- setColumnHeadersTextColor:(NXColor)color
{
	float	gray;

	NXConvertColorToGray(color,&gray);
	[headerTextCell setTextColor:color];
	[headerTextCell setTextGray:gray];
	[rowNumberHeaderCell setTextColor:color];
	[rowNumberHeaderCell setTextGray:gray];
	return self;
}
- (NXColor)columnHeadersTextColor{return [headerTextCell textColor];}

- setColumnHeadersBackColor:(NXColor)color
{
	float	gray;

	NXConvertColorToGray(color,&gray);
	[headerTextCell setBackgroundColor:color];
	[headerTextCell setBackgroundGray:gray];
	[rowNumberHeaderCell setBackgroundColor:color];
	[rowNumberHeaderCell setBackgroundGray:gray];
	return self;
}
- (NXColor)columnHeadersBackColor{return [headerTextCell backgroundColor];}

- setRowNumbersTextColor:(NXColor)color
{
	float	gray;

	NXConvertColorToGray(color,&gray);
	[rowNumberCell setTextColor:color];
	[rowNumberCell setTextGray:gray];
	return self;
}
- (NXColor)rowNumbersTextColor{return [rowNumberCell textColor];}

- setRowNumbersBackColor:(NXColor)color
{
	rowNumbersBackColor = color;
	return self;
}
- (NXColor)rowNumbersBackColor{return rowNumbersBackColor;}

- setGridLinesColor:(NXColor)color
{
	gridColor = color;
	return self;
}
- (NXColor)gridLinesColor{return gridColor;}

- setPageNumberColor:(NXColor)color
{
	float	gray;

	NXConvertColorToGray(color,&gray);
	[pageNumberCell setTextColor:color];
	[pageNumberCell setTextGray:gray];
	return self;
}
- (NXColor)pageNumberColor{return [pageNumberCell textColor];}

- setBackgroundColor:(NXColor)color
{
	backgroundColor = color;
	return self;
}
- (NXColor)backgroundColor{return backgroundColor;}

- printPSCode:sender
{
	id	rv;

	// Print only if table has been set to a valid DBTableView or subclass
	// thereof
	if ( !tableView || ![tableView isKindOfClassNamed:"DBTableView"] )
		return nil;

	// set all instance variables before printing
	[self refreshLayout];
	
	// turn on printing flag and print
	isPrinting = YES;
	rv = [super printPSCode:sender];

	// turn off printing flag and return
	isPrinting = NO;
	return rv;
}

// Setting/Get the current DBTableView
- setTableView:table
{
	tableView = table;
	return self;
}
- tableView{return tableView;}
	
- drawSelf:(const NXRect *)rects :(int)rectCount
{
	id					aVector;
	int				page,subPage,
						i,j,
						colStart,colEnd,colsThisPage;
	unsigned int	row,actualRow,actualPageRowStart,pageRowStart,
						rowNumber,actualRowNumberStart;
	double			tmpDouble;
	NXSize			cellSize;
	NXCoord			theSize;
	char				pageNumberString[50];
	float				gray;
	NXColor			localBackgroundColor = backgroundColor,
						localGridColor = gridColor,
						localRowNumberBackColor = rowNumbersBackColor;
	NXRect			aRect;
	int				bezelDrawOffset = 0,
						bezelForwardOffset = 0;
	BOOL				isBezeledHeaders = NO;

	// don't waste time if this is not a call from printPSCode
	if ( !isPrinting ) return [super drawSelf:rects :rectCount];

	// set an offset for bezeled headers
	if ( [headerTextCell isBezeled] )
	{
		bezelDrawOffset = 5;		// offset to forward origin.y after header
		bezelForwardOffset = 4;	// offset to consider when advancing to next page
		isBezeledHeaders = YES;
	}
	else
	{
		bezelDrawOffset = -1;		// offset to forward origin.y after header
		bezelForwardOffset = -2;// offset to consider when advancing to next page
		isBezeledHeaders = NO;
	}

	// Clear to proper colors.  If drawing in B&W, then convert the gray values
	if ( ![self shouldDrawColor] )
	{
		NXConvertColorToGray(backgroundColor,&gray);
		localBackgroundColor = NXConvertGrayToColor(gray);
		NXConvertColorToGray(gridColor,&gray);
		localGridColor = NXConvertGrayToColor(gray);
		NXConvertColorToGray(rowNumbersBackColor,&gray);
		localRowNumberBackColor = NXConvertGrayToColor(gray);
	}
	NXSetColor(localBackgroundColor);
	NXRectFill(&pageFrame);

	// Now go through and print each full tableview page
	row = 0;
	page = startingPageNumber;
	theRect.origin.y = 0.0; 
	actualRow = 0;
	rowNumber = startingRowNumber;
	while ( actualRow < maxRows )
	{
		colStart = 0;

		// determine how many cols can fit on this page
		for ( colsThisPage = 0,tmpDouble = 0.0,i = colStart;i < maxColumns;i++ )
		{
			[headerTextCell setStringValue:[[columnList objectAt:i] title]];
			[headerTextCell calcCellSize:&cellSize];
			theSize = [(DBTableVector *)[columnList objectAt:i] size];
			if ( theSize < cellSize.width ) theSize = cellSize.width;
			tmpDouble += theSize;
			if ( tmpDouble > rowNumberedWidth ) break;
			else colsThisPage++;
		}
		if ( colsThisPage == 0 ) colsThisPage = 1;

		colEnd = colsThisPage;
		pageRowStart = row;
		actualPageRowStart = actualRow;
		actualRowNumberStart = rowNumber;
		subPage = 1;
		while ( colStart < maxColumns )
		{
			row = pageRowStart;
			actualRow = actualPageRowStart;
			rowNumber = actualRowNumberStart;

			// Draw Page number if required
			if ( isPageNumbersEnabled )
			{
				theRect.origin.y += pageNumberHeight;
				if ( NXPointInRect(&theRect.origin,&rects[0]) )
				{				
					if ( pagesPerRow > 1 )
						sprintf(pageNumberString,"%d%c%d",
							page,pageNumSepChar,subPage);
					else sprintf(pageNumberString,"%d",page);
					[pageNumberCell setStringValue:pageNumberString];
					[pageNumberCell calcCellSize:&cellSize];
					theRect.origin.x = pageFrame.size.width - cellSize.width;
					theRect.size.width = cellSize.width;
					theRect.size.height = pageNumberHeight;
					[pageNumberCell drawSelf:&theRect inView:self];
				}			
			}

			// Initialize the first draw rect for the header row
			theRect.origin.x = 0.0;
			theRect.origin.y += headerTextCellSize.height;
			theRect.size.height = headerTextCellSize.height;

			// Put a header at the top of the page
			if ( NXPointInRect(&theRect.origin,&rects[0]) )
			{
				// Put the Row Number Header if required
				if ( isRowNumbersEnabled )
				{
					theRect.size.width = rowNumberHeaderCellSize.width;
					[rowNumberHeaderCell drawSelf:&theRect inView:self];

					// Draw border around row number header
					if ( isGridLinesOn && !isBezeledHeaders )
					{
						// Set the color of the lines
						NXSetColor(localGridColor);

						// Draw Left, Top, and Right Borders
						PSmoveto(theRect.origin.x,
							theRect.origin.y + rowNumberHeaderCellSize.height);
						PSlineto(theRect.origin.x,theRect.origin.y);
						PSlineto(theRect.origin.x +
							rowNumberHeaderCellSize.width - 1,
							theRect.origin.y);
						PSlineto(theRect.origin.x +
							rowNumberHeaderCellSize.width - 1,
							theRect.origin.y + rowNumberHeaderCellSize.height);
					}

					theRect.origin.x += rowNumberHeaderCellSize.width;
				}
				
				// Put the rest of the header
				for ( i = colStart; i < colEnd; i++ )
				{
					aVector = [columnList objectAt:i];
					[headerTextCell setStringValue:[aVector title]];
					[headerTextCell calcCellSize:&cellSize];
					theSize = [(DBTableVector *)aVector size];
					if ( theSize < cellSize.width ) theSize = cellSize.width;
					theRect.size.width = theSize;
					[headerTextCell setAlignment:[aVector titleAlignment]];
					[headerTextCell drawSelf:&theRect inView:self];

					// Draw border around each header
					if ( isGridLinesOn && !isBezeledHeaders )
					{
						// Set the color of the lines
						NXSetColor(localGridColor);

						// Draw Top and Right Borders
						PSmoveto(theRect.origin.x,theRect.origin.y);
						PSlineto(theRect.origin.x + theRect.size.width - 1,
							theRect.origin.y);
						PSlineto(theRect.origin.x + theRect.size.width - 1,
							theRect.origin.y + theRect.size.height);
					}

					theRect.origin.x += theSize;
				}
			}

			// Now print rows on this page until no more fit
			theRect.size.height = rowHeight;
			theRect.origin.y += bezelDrawOffset;
			for ( i = 0; i < maxRowsPerPage && actualRow < maxRows; row++ )
			{
				// Skip if selection is on and this is not selected
				if ( isSelectedRowsOnly && ![tableView isRowSelected:row] )
					continue;

				// Initialize the first draw rect for the current row
				theRect.origin.x = 0.0;
				theRect.origin.y += rowHeight;

				// Now do each cell by having each formatter draw itself
				if ( NXPointInRect(&theRect.origin,&rects[0]) )
				{
					// Draw a Row Number Cell if required
					if ( isRowNumbersEnabled )
					{
						// Draw the background
						NXSetColor(localRowNumberBackColor);
						NXSetRect(&aRect,theRect.origin.x,theRect.origin.y + 1,
							rowNumberHeaderCellSize.width - 1,rowHeight);
						
						// If the first row, then narrow the gap above the first
						// row number with row number background color
						if ( actualRow == actualPageRowStart )
						{
							aRect.origin.y--;
							aRect.size.height++;
							if ( !isGridLinesOn )
							{
								aRect.origin.y--;
								aRect.size.height++;
							}
						}
						NXRectFill(&aRect);

						// Draw the number using the cell
						[rowNumberCell setIntValue:rowNumber];
						NXSetRect(&aRect,theRect.origin.x,theRect.origin.y,
							rowNumberHeaderCellSize.width,rowHeight);
						[rowNumberCell drawSelf:&aRect inView:self];
						
						// Draw draw lines around row numbers if required
						if ( isGridLinesOn )
						{
							// Set the color of the lines
							NXSetColor(localGridColor);
						
							// Draw Left Border
							PSmoveto(theRect.origin.x,theRect.origin.y);
							PSlineto(theRect.origin.x,theRect.origin.y+rowHeight);
							
							// Always draw bottom and right borders
							PSlineto(theRect.origin.x +
								rowNumberHeaderCellSize.width - 1,
								theRect.origin.y + rowHeight);
							PSlineto(theRect.origin.x +
								rowNumberHeaderCellSize.width - 1,
								theRect.origin.y);
							
							// Only put top border on first rows
							if ( actualRow == actualPageRowStart )
								PSlineto(theRect.origin.x - 1,theRect.origin.y);
						}
						theRect.origin.x += rowNumberHeaderCellSize.width;
					}

					// Draw the rest of the cells
					for ( j = colStart; j < colEnd; j++ )
					{
						[headerTextCell
							setStringValue:[[columnList objectAt:j] title]];
						[headerTextCell calcCellSize:&cellSize];
						theSize = [(DBTableVector *)[columnList objectAt:j] size];
						if ( theSize < cellSize.width ) theSize = cellSize.width;

						theRect.size.width = theSize;
						[[tableView formatterAt:row :j] beginBatching:nil];
						theRect.origin.y++;
						[[tableView formatterAt:row :j] drawFieldAt:row :j
							inside:&theRect inView:self
							withAttributes:[tableView rowAt:row]
							:[tableView columnAt:j] usePositions:YES :NO];
						theRect.origin.y--;
						[[tableView formatterAt:row :j] endBatching];

						// Draw the grid lines if required
						if ( isGridLinesOn )
						{
							// Set the color of the lines
							NXSetColor(localGridColor);
						
							// Only put left borders on first columns and
							// if row numbers are turned off
							if ( j == colStart && !isRowNumbersEnabled )
							{
								PSmoveto(theRect.origin.x,theRect.origin.y);
								PSlineto(theRect.origin.x,theRect.origin.y+rowHeight);
							}
							else
								PSmoveto(theRect.origin.x - 1,
									theRect.origin.y+rowHeight);
							
							// Always draw bottom and right borders
							PSlineto(theRect.origin.x + theRect.size.width - 1,
								theRect.origin.y + rowHeight);
							PSlineto(theRect.origin.x + theRect.size.width - 1,
								theRect.origin.y);
							
							// Only put top border on first rows
							if ( actualRow == actualPageRowStart )
							{
								if ( j == colStart )
									PSlineto(theRect.origin.x,theRect.origin.y);
								else PSlineto(theRect.origin.x - 1,theRect.origin.y);
							}
						}
						theRect.origin.x += theRect.size.width;
					}
					if ( isGridLinesOn || isRowNumbersEnabled ) PSstroke();
				}
				rowNumber++;
				actualRow++;
				i++;
			}
			theRect.origin.y--;
			// advance the draw rect's origin to the next page
			// The bezelForwardOffset is to compensate jumping bezelDrawOffset
			// pixels after drawing the header
			if ( i == maxRowsPerPage )
				theRect.origin.y += pageAdvanceOffset - bezelForwardOffset;
			else
				theRect.origin.y += pageHeight -
					((i * rowHeight) + headerTextCellSize.height +
					pageNumberHeight + bezelForwardOffset);
			colStart = colEnd;

			// determine how many cols can fit on next page
			for ( colsThisPage = 0,tmpDouble = 0.0,i = colStart;
					i < maxColumns; i++ )
			{
				[headerTextCell setStringValue:[[columnList objectAt:i] title]];
				[headerTextCell calcCellSize:&cellSize];
				theSize = [(DBTableVector *)[columnList objectAt:i] size];
				if ( theSize < cellSize.width ) theSize = cellSize.width;
				tmpDouble += theSize;
				if ( tmpDouble > rowNumberedWidth ) break;
				else colsThisPage++;
			}
			if ( colsThisPage == 0 ) colsThisPage = 1;
			colEnd += colsThisPage;
			subPage++;
		}
		page++;
	}

	return self;
}

- read:(NXTypedStream*)stream
{
	int	version;

	[super read:stream];

	// Get the version
	version = NXTypedStreamClassVersion(stream,"DBTableViewPrinter");
	
	// Read all version 0 stuff first
	NXReadTypes(stream,"cccccc@",&isPrinting,&isPageNumbersEnabled,
		&isRowNumbersEnabled,&isSelectedRowsOnly,&isGridLinesOn,
		&isForceDrawColor,&columnList);
	headerTextCell = NXReadObject(stream);
	pageNumberCell = NXReadObject(stream);
	rowNumberHeaderCell = NXReadObject(stream);
	rowNumberCell = NXReadObject(stream);
	NXReadTypes(stream,"c[51c]",&pageNumSepChar,&rowNumberHeaderTitle);
	NXReadTypes(stream,"{ffff}",&pageFrame);
	NXReadTypes(stream,"{ffff}",&theRect);
	NXReadTypes(stream,"fffff",&rowHeight,&pageHeight,
		&pageAdvanceOffset,&pageNumberHeight,&rowNumberedWidth);
	NXReadTypes(stream,"{ff}{ff}{ff}",
		&headerTextCellSize,&pageNumberCellSize,&rowNumberHeaderCellSize);
	NXReadTypes(stream,"iiiiii",
		&maxColumns,&maxRows,&maxRowsPerPage,&pagesPerRow,
		&startingPageNumber,&startingRowNumber);
	gridColor = NXReadColor(stream);
	backgroundColor = NXReadColor(stream);
	NXReadTypes(stream,"@",&tableView);

	// Read Version 1 stuff
	if ( version > 0 ) rowNumbersBackColor = NXReadColor(stream);

	return self;
}

- write:(NXTypedStream*)stream
{
	[super write:stream];

	// Write version 0 stuff first
	NXWriteTypes(stream,"cccccc@",&isPrinting,&isPageNumbersEnabled,
		&isRowNumbersEnabled,&isSelectedRowsOnly,&isGridLinesOn,
		&isForceDrawColor,&columnList);
	NXWriteObject(stream,headerTextCell);
	NXWriteObject(stream,pageNumberCell);
	NXWriteObject(stream,rowNumberHeaderCell);
	NXWriteObject(stream,rowNumberCell);
	NXWriteTypes(stream,"c[51c]",&pageNumSepChar,&rowNumberHeaderTitle);
	NXWriteTypes(stream,"{ffff}",&pageFrame);
	NXWriteTypes(stream,"{ffff}",&theRect);
	NXWriteTypes(stream,"fffff",&rowHeight,&pageHeight,
		&pageAdvanceOffset,&pageNumberHeight,&rowNumberedWidth);
	NXWriteTypes(stream,"{ff}{ff}{ff}",
		&headerTextCellSize,&pageNumberCellSize,&rowNumberHeaderCellSize);
	NXWriteTypes(stream,"iiiiii",
		&maxColumns,&maxRows,&maxRowsPerPage,&pagesPerRow,
		&startingPageNumber,&startingRowNumber);
	NXWriteColor(stream,gridColor);
	NXWriteColor(stream,backgroundColor);
	NXWriteTypes(stream,"@",&tableView);

	// write version 1 stuff
	NXWriteColor(stream,rowNumbersBackColor);

	return self;
}

@end

@implementation DBTableViewPrinter(Private)

// refreshLayout gets the page size and determines all size parameters like
// the number of rows per page, the number of columns per page, etc.

- refreshLayout
{
	int				i,
						bezelDrawOffset = 0,
						maxPages;
	NXRect			visibleRect;
	NXSize			cellSize;
	NXCoord			theSize,
						leftMargin,rightMargin,topMargin,bottomMargin;
	const NXRect	*paperRect;
	double			tmpDouble;
	char				title[51];
	id					thePrintPanel = nil;

	// Get some formatter and size information from the tableView
	columnList = [tableView columnList];
	maxColumns = [columnList count];
	if ( isSelectedRowsOnly ) maxRows = [tableView selectedRowCount];
	else maxRows = [tableView rowCount];
	if ( [tableView rowCount] > 0 ) rowHeight = [[tableView rowAt:0] size];
	if ( rowHeight <= 0.0 ) rowHeight = 18;

	// Lock the table's window and make sure everything has been displayed
	// if everything has not been displayed, it will not get printed, so I
	// have to "force a dislay" here, even if it is off screen.
	[[tableView window] disableFlushWindow];
	[[[tableView docView] superview] getDocVisibleRect:&visibleRect];
	for ( i = 0; i < maxColumns; i++ )
		[(DBTableView *)tableView scrollColumnToVisible:i];
	[tableView scrollClip:[tableView docView] to:&visibleRect.origin];
	[tableView reflectScroll:[[tableView docView] superview]];
	[tableView layoutChanged:self];
	[[tableView window] reenableFlushWindow];

	// Get the width of a page
	[[NXApp printInfo] getMarginLeft:&leftMargin right:&rightMargin
		top:&topMargin bottom:&bottomMargin];
	paperRect = [[NXApp printInfo] paperRect];
	pageFrame.size.width =
		(NXCoord)paperRect->size.width - (NXCoord)(rightMargin + leftMargin);
	rowNumberedWidth = pageFrame.size.width;

	// If row numbering is on, set the row header cell to the correct width
	if ( isRowNumbersEnabled )
	{
		// Get the size of the number of digits in maxRow
		[rowNumberHeaderCell setIntValue:(maxRows + startingRowNumber) * 10];
		[rowNumberHeaderCell calcCellSize:&cellSize];

		// Initialize the rowHeader sizes and values
		[rowNumberHeaderCell setStringValue:rowNumberHeaderTitle];
		[rowNumberHeaderCell calcCellSize:&rowNumberHeaderCellSize];

		// Keep making the Row Header wider until it's wide enough
		memset(title,0,51);
		strncpy(title,rowNumberHeaderTitle,50);
		while ( rowNumberHeaderCellSize.width < cellSize.width &&
				strlen(title) < 45 )
		{
			sprintf(title," %s ",[rowNumberHeaderCell stringValue]);
			[rowNumberHeaderCell setStringValue:title];
			[rowNumberHeaderCell calcCellSize:&rowNumberHeaderCellSize];
		}
		rowNumberedWidth -= rowNumberHeaderCellSize.width;
	}

	// Get total pages to print a row
	for ( pagesPerRow = 1, tmpDouble = 0.0, i = 0; i < maxColumns; i++ )
	{
		[headerTextCell setStringValue:[[columnList objectAt:i] title]];
		[headerTextCell calcCellSize:&cellSize];
		theSize = [(DBTableVector *)[columnList objectAt:i] size];
		if ( theSize < cellSize.width ) theSize = cellSize.width;
		tmpDouble += theSize;
		if ( tmpDouble > rowNumberedWidth )
		{
			if ( i < (maxColumns - 1) && !(theSize > rowNumberedWidth &&
				tmpDouble == theSize) ) i--;
			pagesPerRow++;
			tmpDouble = 0.0;
		}
	}

	// Set the height of the page number cell if desired
	if ( isPageNumbersEnabled ) pageNumberHeight = pageNumberCellSize.height;
	else pageNumberHeight = 0.0; 

	// offset to forward origin.y after drawing each header
	if ( [headerTextCell isBezeled] ) bezelDrawOffset = 5;

	// Get the single page height and the whole rect needed to print this thing
	pageHeight = paperRect->size.height - (topMargin + bottomMargin);
	tmpDouble =
		(double)((pageHeight - headerTextCellSize.height) -
		(pageNumberHeight + bezelDrawOffset)) / (double)rowHeight;
	maxRowsPerPage = (int)floor(tmpDouble) - 1;
	tmpDouble = (double)maxRows / (double)maxRowsPerPage;
	maxPages = (int)ceil(tmpDouble) * pagesPerRow;
	if ( maxPages <= 0 ) maxPages = 1;
	pageFrame.size.height = (NXCoord)maxPages * (NXCoord)pageHeight;

	// Size the View in which to draw
	[self sizeTo:pageFrame.size.width :pageFrame.size.height];

	// Get the offset to add to advance to the next page
	pageAdvanceOffset = pageHeight - ((maxRowsPerPage * rowHeight) +
		headerTextCellSize.height + pageNumberHeight);

	// Get my subclass of print panel and print with the proper pagesPerRow
	thePrintPanel = [TablePrintPanel new];
	if ( thePrintPanel &&
			[thePrintPanel respondsTo:@selector(setPagesPerRow:)] )
		[thePrintPanel setPagesPerRow:pagesPerRow];

	return self;
}

@end