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.