This is MOMatrix.m in view mode; [Download] [Up]
// MOMatrix.m
//
// by Mike Ferris
// Part of MOKit
// Copyright 1993, all rights reserved.
// ABOUT MOKit
// by Mike Ferris (mike@lorax.com)
//
// MOKit is a collection of useful and general objects. Permission is
// granted by the author to use MOKit in your own programs in any way
// you see fit. All other rights pertaining to the kit are reserved by the
// author including the right to sell these objects as objects, as part
// of a LIBRARY, or as SOURCE CODE. In plain English, I wish to retain
// rights to these objects as objects, but allow the use of the objects
// as pieces in a fully functional program. Permission is also granted to
// redistribute the source code of MOKit for FREE as long as this copyright
// notice is left intact and unchanged. NO WARRANTY is expressed or implied.
// The author will under no circumstances be held responsible for ANY
// consequences from the use of these objects. Since you don't have to pay
// for them, and full source is provided, I think this is perfectly fair.
#import "MOKit/MOMatrix.h"
#import <objc/objc-runtime.h>
#define CLASS_VERSION 0
#define CLASS_NAME "MOMatrix"
@implementation MOMatrix
+ initialize
// Set the class version
{
if (self == objc_lookUpClass(CLASS_NAME)) {
[self setVersion:CLASS_VERSION];
}
return self;
}
- setupStorage:(int)rowsHigh :(int)colsWide
// Set up our storage objects
{
int i;
ColumnSize newCSize;
RowSize newRSize;
columnSizes = [[Storage allocFromZone:[self zone]] initCount:0
elementSize:sizeof(ColumnSize) description:COLUMNSIZE_DESC];
rowSizes = [[Storage allocFromZone:[self zone]] initCount:0
elementSize:sizeof(RowSize) description:ROWSIZE_DESC];
newCSize.width = cellSize.width;
newRSize.height = cellSize.height;
for (i=0; i<colsWide; i++) {
newCSize.x = bounds.origin.x + (i*cellSize.width) +
(i*intercell.width);
[columnSizes addElement:&newCSize];
}
for (i=0; i<rowsHigh; i++) {
newRSize.y = bounds.origin.y + (i*cellSize.height) +
(i*intercell.height);
[rowSizes addElement:&newRSize];
}
return self;
}
- initFrame:(const NXRect *)frm mode:(int)aMode prototype:cellId
numRows:(int)rowsHigh numCols:(int)colsWide
// Designated initializer override from Matrix. Sets up our storage stuff.
{
[super initFrame:frm mode:aMode prototype:cellId numRows:numRows
numCols:numCols];
[self setupStorage:rowsHigh :colsWide];
return self;
}
- initFrame:(const NXRect *)frm mode:(int)aMode cellClass:factoryId
numRows:(int)rowsHigh numCols:(int)colsWide
// Designated initializer override from Matrix. Sets up our storage stuff.
{
[super initFrame:frm mode:aMode cellClass:factoryId numRows:numRows
numCols:numCols];
[self setupStorage:rowsHigh :colsWide];
return self;
}
- MO_copyRowSizes:rs andColSizes:cs zone:(NXZone *)zone
{
rowSizes = [rs copyFromZone:zone];
columnSizes = [cs copyFromZone:zone];
return self;
}
- copyFromZone:(NXZone *)zone
{
id obj = [super copyFromZone:zone];
[obj MO_copyRowSizes:rowSizes andColSizes:columnSizes zone:zone];
return obj;
}
- free
// free the storage
{
[columnSizes free];
[rowSizes free];
return [super free];
}
- MO_moveColumnsRightBy:(NXCoord)difference startingAt:(int)col
// A private method used by the methods which cause sizing stuff to change
{
int i;
if ((col < 0) || (col >= numCols)) {
return nil;
}
for (i=col; i<numCols; i++) {
ColumnSize *cSize;
cSize = (ColumnSize *)[columnSizes elementAt:i];
if (cSize) cSize->x += difference;
}
return self;
}
- MO_moveRowsDownBy:(NXCoord)difference startingAt:(int)row
// A private method used by the methods which cause sizing stuff to change
{
int i;
if ((row < 0) || (row >= numRows)) {
return nil;
}
for (i=row; i<numRows; i++) {
RowSize *rSize;
rSize = (RowSize *)[rowSizes elementAt:i];
if (rSize) rSize->y += difference;
}
return self;
}
- setWidth:(NXCoord)newWidth ofCol:(int)col
// This method allows the setting of column widths
{
NXCoord diff;
ColumnSize *cSize;
if ((col < 0) || (col >= numCols)) {
return nil;
}
cSize = (ColumnSize *)[columnSizes elementAt:col];
diff = newWidth - cSize->width;
cSize->width = newWidth;
[self MO_moveColumnsRightBy:diff startingAt:col+1];
return self;
}
- setHeight:(NXCoord)newHeight ofRow:(int)row
// This method allows the setting of row heights
{
NXCoord diff;
RowSize *rSize;
if ((row < 0) || (row >= numRows)) {
return nil;
}
rSize = (RowSize *)[rowSizes elementAt:row];
diff = newHeight - rSize->height;
rSize->height = newHeight;
[self MO_moveRowsDownBy:diff startingAt:row+1];
return self;
}
- sizeToCells
// Resize the matrix to the proper size to fit all our cells.
{
NXRect rect;
ColumnSize *cSize;
RowSize *rSize;
[self getFrame:&rect];
if (numCols == 0) {
rect.size.width = 0.0;
} else {
cSize = (ColumnSize *)[columnSizes MO_lastElement];
rect.size.width = cSize->x + cSize->width - bounds.origin.x;
}
if (numRows == 0) {
rect.size.height = 0.0;
} else {
rSize = (RowSize *)[rowSizes MO_lastElement];
rect.size.height = rSize->y + rSize->height - bounds.origin.y;
}
[self setFrame:&rect];
return self;
}
- renewRows:(int)newRows cols:(int)newCols
// Makes sure to keep our storage objects in synch with everything else.
{
ColumnSize newCSize, *cSize;
RowSize newRSize, *rSize;
int i;
// Remove any storage elements past the new number of cols
for (i=numCols-1; i>=newCols; i--) {
[columnSizes removeLastElement];
}
// Add any needed new storage elements to get up to the new number of cols
for (i=numCols; i<newCols; i++) {
if (i==0) {
newCSize.x = bounds.origin.x;
} else {
cSize = (ColumnSize *)[columnSizes MO_lastElement];
newCSize.x = cSize->x + cSize->width + intercell.width;
}
newCSize.width = cellSize.width;
[columnSizes addElement:&newCSize];
}
// Remove any storage elements past the new number of rows
for (i=numRows-1; i>=newRows; i++) {
[rowSizes removeLastElement];
}
// Add any needed new storage elements to get up to the new number of rows
for (i=numRows; i<newRows; i++) {
if (i==0) {
newRSize.y = bounds.origin.y;
} else {
rSize = (RowSize *)[rowSizes MO_lastElement];
newRSize.y = rSize->y + rSize->height + intercell.height;
}
newRSize.height = cellSize.height;
[rowSizes addElement:&newRSize];
}
[super renewRows:newRows cols:newCols];
return self;
}
- addCol
// Keep the storage in synch
{
ColumnSize newCSize, *cSize;
[super addCol];
if (numCols > 0) {
cSize = (ColumnSize *)[columnSizes MO_lastElement];
newCSize.x = cSize->x + cSize->width + intercell.width;
} else {
newCSize.x = bounds.origin.x;
}
newCSize.width = cellSize.width;
[columnSizes addElement:&newCSize];
return self;
}
- addRow
// Keep the storage in synch
{
RowSize newRSize, *rSize;
[super addRow];
if (numRows > 0) {
rSize = (RowSize *)[rowSizes MO_lastElement];
newRSize.y = rSize->y + rSize->height + intercell.height;
} else {
newRSize.y = bounds.origin.y;
}
newRSize.height = cellSize.height;
[rowSizes addElement:&newRSize];
return self;
}
- insertColAt:(int)col
// Keep the storage in synch
{
ColumnSize newCSize, *cSize;
if (col < 0) {
return nil;
}
if (col <= numCols) {
[super insertColAt:col];
if (col > 0) {
cSize = (ColumnSize *)[columnSizes elementAt:col];
newCSize.x = cSize->x;
} else {
newCSize.x = bounds.origin.x;
}
newCSize.width = cellSize.width;
[columnSizes insertElement:&newCSize at:col];
[self MO_moveColumnsRightBy:newCSize.width + intercell.width
startingAt:col+1];
} else {
return nil;
}
return self;
}
- insertRowAt:(int)row
// Keep the storage in synch
{
RowSize newRSize, *rSize;
if (row < 0) {
return nil;
}
if (row <= numRows) {
[super insertRowAt:row];
if (row > 0) {
rSize = (RowSize *)[rowSizes elementAt:row];
newRSize.y = rSize->y;
} else {
newRSize.y = bounds.origin.y;
}
newRSize.height = cellSize.height;
[rowSizes insertElement:&newRSize at:row+1];
[self MO_moveRowsDownBy:newRSize.height + intercell.height
startingAt:row+1];
} else {
return nil;
}
return self;
}
- removeColAt:(int)col andFree:(BOOL)flag
// Keep the storage in synch
{
ColumnSize *cSize;
NXCoord diff;
if ((col >= numCols) || (col < 0)) {
return nil;
}
[super removeColAt:col andFree:flag];
cSize = (ColumnSize *)[columnSizes elementAt:col];
diff = cSize->width;
[columnSizes removeElementAt:col];
[self MO_moveColumnsRightBy:0.0 - diff - intercell.width startingAt:col];
return self;
}
- removeRowAt:(int)row andFree:(BOOL)flag
// Keep the storage in synch
{
RowSize *rSize;
NXCoord diff;
if ((row >= numRows) || (row < 0)) {
return nil;
}
[super removeRowAt:row andFree:flag];
rSize = (RowSize *)[rowSizes elementAt:row];
diff = rSize->height;
[rowSizes removeElementAt:row];
[self MO_moveRowsDownBy:0.0 - diff - intercell.height startingAt:row];
return self;
}
- drawSelf:(const NXRect *)rects:(int)rectCount
// We do our own drawing because we need to draw our cells in diverse
// rectangles
{
int i, j;
NXRect cFrm;
[window disableFlushWindow];
// the background (if any)
if (backgroundGray != -1.0) {
PSsetgray(backgroundGray);
if (rectCount==1) {
NXRectFill(&(rects[0]));
} else {
NXRectFill(&(rects[1]));
NXRectFill(&(rects[2]));
}
}
// The cells
for (i=0; i<numRows; i++) {
for (j=0; j<numCols; j++) {
[self getCellFrame:&cFrm at:i:j];
if (rectCount == 1) {
if (NXIntersectsRect(&(rects[0]), &cFrm)) {
[self drawCellAt:i:j];
}
} else {
if ((NXIntersectsRect(&(rects[1]), &cFrm)) ||
(NXIntersectsRect(&(rects[2]), &cFrm))) {
[self drawCellAt:i:j];
}
}
}
}
[window reenableFlushWindow];
[window flushWindow];
return self;
}
- getCellFrame:(NXRect *)theRect at:(int)row:(int)col
// Calculate and return the rect used to display the cell at the given
// row and column
{
ColumnSize *cSize;
RowSize *rSize;
if (col < numCols) {
cSize = (ColumnSize *)[columnSizes elementAt:col];
theRect->origin.x = cSize->x;
theRect->size.width = cSize->width;
} else {
int num = col - numCols;
cSize = (ColumnSize *)[columnSizes MO_lastElement];
theRect->origin.x = cSize->x +
(num * (cellSize.width + intercell.width));
theRect->size.width = cellSize.width;
}
if (row < numRows) {
rSize = (RowSize *)[rowSizes elementAt:row];
theRect->origin.y = rSize->y;
theRect->size.height = rSize->height;
} else {
int num = row - numRows;
rSize = (RowSize *)[rowSizes MO_lastElement];
theRect->origin.y = rSize->y +
(num * (cellSize.height + intercell.height));
theRect->size.height = cellSize.height;
}
return self;
}
- getRow:(int *)row andCol:(int *)col forPoint:(const NXPoint *)aPoint
// Calculate the row and column of the cell which contains the given point
{
ColumnSize *cSize;
RowSize *rSize;
int i;
*row = -1;
*col = -1;
if ((aPoint->x < bounds.origin.x) ||
(aPoint->x > bounds.origin.x + bounds.size.width) ||
(aPoint->y < bounds.origin.y) ||
(aPoint->y > bounds.origin.y + bounds.size.height)) {
return self;
}
for (i=0; i<numCols; i++) {
cSize = (ColumnSize *)[columnSizes elementAt:i];
if ((aPoint->x >= cSize->x) &&
(aPoint->x <= cSize->x + cSize->width)) {
*col = i;
break;
}
}
for (i=0; i<numRows; i++) {
rSize = (RowSize *)[rowSizes elementAt:i];
if ((aPoint->y >= rSize->y) &&
(aPoint->y <= rSize->y + rSize->height)) {
*row = i;
break;
}
}
return self;
}
- setIntercell:(const NXSize *)aSize
// Keep the storage in synch
{
NXCoord xDiff = aSize->width - intercell.width;
NXCoord yDiff = aSize->height - intercell.height;
RowSize *rSize;
ColumnSize *cSize;
int i;
for (i=1; i<numRows; i++) {
rSize = (RowSize *)[rowSizes elementAt:i];
rSize->y += (yDiff * i);
}
for (i=1; i<numCols; i++) {
cSize = (ColumnSize *)[columnSizes elementAt:i];
cSize->x += (xDiff * i);
}
return [super setIntercell:aSize];
}
- write:(NXTypedStream *)typedStream
// Write our ivars
{
[super write:typedStream];
NXWriteObject(typedStream, columnSizes);
NXWriteObject(typedStream, rowSizes);
return self;
}
- read:(NXTypedStream *)typedStream
// Read our ivars
{
int classVersion;
[super read:typedStream];
classVersion = NXTypedStreamClassVersion(typedStream, CLASS_NAME);
switch (classVersion) {
case 0: // First version.
columnSizes = NXReadObject(typedStream);
rowSizes = NXReadObject(typedStream);
break;
default:
NXLogError("[%s read:] class version %d cannot read "
"instances archived with version %d",
CLASS_NAME, CLASS_VERSION, classVersion);
[self setupStorage:numRows :numCols];
break;
}
return self;
}
// ********************Overridden private methods***********************
// *****************that I'm going to hell for using********************
// These methods are used by Matrix's mouseDown:. Doing the whole
// mouseDown: method over would have been a royal pain in the butt,
// so I cheated.
- (BOOL)_mouseHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
{
NXPoint point;
id ret;
point = *forpoint;
[self convertPoint:&point fromView:nil];
ret = [self getRow:row andCol:col forPoint:&point];
if (ret == nil)
return NO;
return YES;
}
- (BOOL)_loopHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
{
NXPoint point;
id ret;
point = *forpoint;
[self convertPoint:&point fromView:nil];
ret = [self getRow:row andCol:col forPoint:&point];
if (ret == nil)
return NO;
return YES;
}
- (BOOL)_radioHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
{
NXPoint point;
id ret;
point = *forpoint;
[self convertPoint:&point fromView:nil];
ret = [self getRow:row andCol:col forPoint:&point];
if (ret == nil)
return NO;
return YES;
}
@end
@implementation Storage(MOLastElementCategory)
- (void *)MO_lastElement
// A little shortcut
{
return [self elementAt:numElements-1];
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.