

/* A subclass of Matrix that allows variable size columns and rows */
/* Creator : Imran Qureshi */
/* August 3, 1992 */
/* e-mail : cooldude@leland.stanford.edu */
/*  Provided as FREEWARE  */

#import "VarMatrix.h"
#import <appkit/TextFieldCell.h>
#import <objc/List.h>
#import <stdlib.h>

@implementation VarMatrix

-initFrame:(NXRect *)frameRect mode:(int)aMode prototype:aCell numRows:(int)rowsHigh numCols:(int)colsWide;
{
	int i;
	column	*thisColPtr;
	row		*thisRowPtr;
	
	[super initFrame:frameRect mode:aMode prototype:aCell 
							numRows:rowsHigh numCols:colsWide];
	
	columnList = [[List alloc] init];
	rowList = [[List alloc] init];
	
	for (i=0; i < colsWide; i++) {
		thisColPtr = (column*)calloc(1,sizeof(column));
		[columnList addObject:(id)thisColPtr];
		thisColPtr->x = i * cellSize.width;
		thisColPtr->width = cellSize.width;
	}
	
	for (i=0; i < rowsHigh; i++) {
		thisRowPtr = (row*)calloc(1,sizeof(row));
		[rowList addObject:(id)thisRowPtr];
		thisRowPtr->y = i * cellSize.height;
		thisRowPtr->height = cellSize.height;
	}
	{
		NXRect	rect;
									 
		[self getFrame:&rect];
		rect.size.width =130.;
		NXSetRect(&oldRect,rect.origin.x,rect.origin.y,
									 rect.size.width, rect.size.height);

		[self setFrame:&rect];
	}
	return self;
}
-erase
{
	NXRect	rect;
	
	[self lockFocus];
	[self getFrame:&rect];
	NXEraseRect(&rect);
	[self unlockFocus];
	return self;
}
-setWidth:(float)width forCol:(int)col
{
	float	diff;
	
	
	diff = width - ((column*)[columnList objectAt:col])->width;
	((column*)[columnList objectAt:col])->width = width;
	
	[self updateColsFrom:col+1 with:diff];
	
	
	return self;
}
-setHeight:(float)height forRow:(int)theRow
{
	float	diff;
	
	
	diff = height - ((row*)[rowList objectAt:theRow])->height;
	((row*)[rowList objectAt:theRow])->height = height;
	
	[self updateRowsFrom:theRow+1 with:diff];
	
	return self;
}
-sizeToCells
{
	NXRect	rect;
									 
	[self getFrame:&rect];
	NXSetRect(&oldRect,rect.origin.x,rect.origin.y,
									 rect.size.width, rect.size.height);
	rect.size.width = ((column*)[columnList lastObject])->x +
									 ((column*)[columnList lastObject])->width;
	rect.size.height = ((row*)[rowList lastObject])->y +
									 ((row*)[rowList lastObject])->height;
	[self setFrame:&rect];
	
	return self;

}
-renewRows:(int)newRows cols:(int)newCols
{
	column	*rectPtr;
	row		*rowPtr;
	int		i;
	
	for (i=numCols; i < newCols; i++) {
		rectPtr = (column*)calloc(1,sizeof(column));
		[columnList addObject:(id)rectPtr];
		rectPtr->x = i * cellSize.width;
		rectPtr->width = cellSize.width;
	
	}
	for (i=numRows; i < newRows; i++) {
		rowPtr = (row*)calloc(1,sizeof(row));
		[rowList addObject:(id)rowPtr];
		rowPtr->y = i * cellSize.height;
		rowPtr->height = cellSize.height;
	
	}

	[super renewRows:newRows cols:newCols];
	return self;
}
-addCol
{
	column	*rectPtr;
	float	place;
	
	rectPtr = (column*)calloc(1,sizeof(column));
	place = ((column*)[columnList lastObject])->x +
									 ((column*)[columnList lastObject])->width;
		
	[columnList addObject:(id)rectPtr];
	rectPtr->x = place;
	rectPtr->width = cellSize.width;
	
	[super addCol];
	return self;
}
- insertColAt:(int)col
{
	column		*rectPtr;
	float	place,diff;
	
	rectPtr = (column*)calloc(1,sizeof(column));
	place = ((column*)[columnList objectAt:col-1])->x +
							((column*)[columnList objectAt:col-1])->width;
		
	[columnList insertObject:(id)rectPtr at:col];
	rectPtr->x = place;
	rectPtr->width = cellSize.width;

	diff = cellSize.width;
	
	[self updateColsFrom:col+1 with:diff];
	
	return [super insertColAt:col];
	
}
- removeColAt:(int)col andFree:(BOOL)flag
{
	float	diff;
	
	/*  is negative 'cause we want to subtract this height from the rest */
	diff = -((column*)[columnList objectAt:col])->width;
	[columnList removeObjectAt:col];
	
	[self updateColsFrom:col with: diff];
	
	return [super removeColAt:col andFree:flag];
}
-addRow
{
	row		*rectPtr;
	float	place;
	
	rectPtr = (row*)calloc(1,sizeof(row));
	place = ((row*)[rowList lastObject])->y +
									 ((row*)[rowList lastObject])->height;
		
	[rowList addObject:(id)rectPtr];
	rectPtr->y = place;
	rectPtr->height = cellSize.height;
	
	return [super addRow];
}
- insertRowAt:(int)theRow
{
	row		*rectPtr;
	float	place,diff;
	
	rectPtr = (row*)calloc(1,sizeof(row));
	place = ((row*)[rowList objectAt:theRow-1])->y +
							((row*)[rowList objectAt:theRow-1])->height;
		
	[rowList insertObject:(id)rectPtr at:theRow];
	rectPtr->y = place;
	rectPtr->height = cellSize.height;

	diff = cellSize.width;
	
	[self updateRowsFrom:theRow+1 with:diff];
	
	return [super insertRowAt:theRow];
	
}
- removeRowAt:(int)theRow andFree:(BOOL)flag
{
	float	diff;
	
	/*  is negative 'cause we want to subtract this height from the rest */
	diff = -((row*)[rowList objectAt:theRow])->height;
	[rowList removeObjectAt:theRow];
	
	[self updateRowsFrom:theRow with: diff];
	
	return [super removeRowAt:theRow andFree:flag];
}

-updateRowsFrom:(int)rowNum with:(float)diff
{
	int		i;
	for (i=rowNum; i < [rowList count] ; i++) {
		((row*)[rowList objectAt:i])->y += diff;
	}
	return self;
}
-updateColsFrom:(int)colNum with:(float)diff
{
	int		i;
	for (i=colNum; i < [columnList count] ; i++) {
		((column*)[columnList objectAt:i])->x += diff;
	}
	return self;
}

-update
{
	int	col,row;
	NXRect	rect;
	
	//printf("update\n");
	if ([self superview] == NULL) return [super update];
	[self erase];
	for (col = 0; col < [columnList count]; col++) {
		for (row = 0; row < [rowList count]; row++) {
			[self drawCellAt:row :col];
		}
	}
	
	[self getFrame:&rect];
	NXSetRect(&oldRect,rect.origin.x,rect.origin.y,
									 rect.size.width, rect.size.height);
	return self;
}
-drawCellAt:(int)theRow :(int)col
{
	NXRect	cellFrame = {0.0,0.0,20.0,20.0};
	//printf("drawCellAt: %d :%d\n",theRow,col);
	//[super drawCellAt:row :col];
	cellFrame.origin.x = ((column*)[columnList objectAt:col])->x;
	cellFrame.origin.y = ((row *)[rowList objectAt:theRow])->y;
	cellFrame.size.width =((column*)[columnList objectAt:col])->width;
	cellFrame.size.height =((row*)[rowList objectAt:theRow])->height;
	[self lockFocus];
	
	[[self cellAt:theRow :col] drawSelf:&cellFrame inView:self];
	[self unlockFocus];
	return self;
}
-drawSelf:(const NXRect *)rects :(int)rectCount
{
	NXRect	rect;
	
	//printf("drawSelf: %d\n",rectCount);
	[self getFrame:&rect];
	NXEraseRect(&rect);
	
	[super drawSelf:rects :rectCount];
	return self;
}
-getCellFrame:(NXRect *)theRect at:(int)theRow :(int)col
{
	//printf("getCellFrame at:%d :%d\n",theRow,col);
	//[super getCellFrame:theRect at:row :col];
	
	NXSetRect(theRect,((column*)[columnList objectAt:col])->x,
			((row *)[rowList objectAt:theRow])->y,
			((column*)[columnList objectAt:col])->width,
			((row*)[rowList objectAt:theRow])->height);
	return self;
}
-getRow:(int *)theRow andCol:(int*)col forPoint:(const NXPoint *)point
{
	int 	i;
	//printf("getRow\n");
	
	*theRow = -1;
	*col = -1;
	
	for (i = 0; i < [columnList count]; i++) {
		if ((point->x >= (((column*)[columnList objectAt:i])->x)) &&
			(point->x <= ((((column*)[columnList objectAt:i])->x) +
				 ((column*)[columnList objectAt:i])->width))) {
			*col =i;
		}
	}
	for (i = 0; i < [rowList count]; i++) {
		if ((point->y >= (((row*)[rowList objectAt:i])->y)) &&
			(point->y <= ((((row*)[rowList objectAt:i])->y) +
				 ((row*)[rowList objectAt:i])->height))) {
			*theRow =i;
		}
	}
	return self;
}
-getRow:(int *)row andCol:(int *)col ofCell:aCell
{
	//printf("getRow for cell\n");
	//[aCell getDrawRect:&rect];
	//*row = abs(rect.origin.y/20.0);
	//*col = abs(rect.origin.x/20.0);
	[super getRow:row andCol:col ofCell:aCell];
	return self;
}
- mouseDown:(NXEvent *)theEvent
{
    NXPoint		mouseDownLocation;
    int			row, column;
    id			activeCell;
      
	//printf("mouseDown:\n");
  /* find the cell that got clicked on and select it */
    mouseDownLocation = theEvent->location;
    [self convertPoint:&mouseDownLocation fromView:nil];
    [self getRow:&row andCol:&column forPoint:&mouseDownLocation];
    activeCell = [self cellAt:row :column];
    [self selectCell:activeCell];
    //[self getCellFrame:&cellFrame at:row :column];
    
  /* do whatever's required for a single-click */
    [self sendAction];
    
  	return self;
}

-write:(NXTypedStream*)typedStream
{
	[super write:typedStream];
	NXWriteObject(typedStream,rowList);
	NXWriteObject(typedStream,columnList);
	return self;
}
-read:(NXTypedStream*)typedStream
{
	[super read:typedStream];
	
	rowList = NXReadObject(typedStream);
	columnList = NXReadObject(typedStream);
	return self;
}

@end
