This is MiscMatrix.m in view mode; [Download] [Up]
// // MiscMatrix.m -- a class to implement variable-sized matrices // Written by Mike Ferris (c) 1994 by Mike Ferris. // Modified from original MOKit "MOMatrix" class by Don Yacktman. // Version 1.0. All rights reserved. // // This notice may not be removed from this source code. // // This object is included in the MiscKit by permission from the author // and its use is governed by the MiscKit license, found in the file // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file // for a list of all applicable permissions and restrictions. // // MiscMatrix is a subclass of Matrix that allows independantly sizable // rows and columns. Each row can have a different height and each column // can have a different width. #import <misckit/MiscMatrix.h> #import <objc/objc-runtime.h> #define CLASS_VERSION 0 #define CLASS_NAME "MiscMatrix" // These are the private methods we use in MiscMatrix @interface MiscMatrix(private) - _Misc_copyRowSizes:rs andColSizes:cs zone:(NXZone *)zone; - _Misc_moveColumnsRightBy:(NXCoord)difference startingAt:(int)col; - _Misc_moveRowsDownBy:(NXCoord)difference startingAt:(int)row; @end @implementation MiscMatrix + 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; MiscColumnSize newCSize; MiscRowSize newRSize; columnSizes = [[Storage allocFromZone:[self zone]] initCount:0 elementSize:sizeof(MiscColumnSize) description:MISC_COLUMNSIZE_DESC]; rowSizes = [[Storage allocFromZone:[self zone]] initCount:0 elementSize:sizeof(MiscRowSize) description:MISC_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; } - _Misc_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 _Misc_copyRowSizes:rowSizes andColSizes:columnSizes zone:zone]; return obj; } - free // free the storage { [columnSizes free]; [rowSizes free]; return [super free]; } - _Misc_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++) { MiscColumnSize *cSize; cSize = (MiscColumnSize *)[columnSizes elementAt:i]; if (cSize) cSize->x += difference; } return self; } - _Misc_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++) { MiscRowSize *rSize; rSize = (MiscRowSize *)[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; MiscColumnSize *cSize; if ((col < 0) || (col >= numCols)) { return nil; } cSize = (MiscColumnSize *)[columnSizes elementAt:col]; diff = newWidth - cSize->width; cSize->width = newWidth; [self _Misc_moveColumnsRightBy:diff startingAt:col+1]; return self; } - setHeight:(NXCoord)newHeight ofRow:(int)row // This method allows the setting of row heights { NXCoord diff; MiscRowSize *rSize; if ((row < 0) || (row >= numRows)) { return nil; } rSize = (MiscRowSize *)[rowSizes elementAt:row]; diff = newHeight - rSize->height; rSize->height = newHeight; [self _Misc_moveRowsDownBy:diff startingAt:row+1]; return self; } - sizeToCells // Resize the matrix to the proper size to fit all our cells. { NXRect rect; MiscColumnSize *cSize; MiscRowSize *rSize; [self getFrame:&rect]; if (numCols == 0) { rect.size.width = 0.0; } else { cSize = (MiscColumnSize *)[columnSizes lastElement]; rect.size.width = cSize->x + cSize->width - bounds.origin.x; } if (numRows == 0) { rect.size.height = 0.0; } else { rSize = (MiscRowSize *)[rowSizes 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. { MiscColumnSize newCSize, *cSize; MiscRowSize 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 = (MiscColumnSize *)[columnSizes 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 = (MiscRowSize *)[rowSizes lastElement]; newRSize.y = rSize->y + rSize->height + intercell.height; } newRSize.height = cellSize.height; [rowSizes addElement:&newRSize]; } [super renewRows:newRows cols:newCols]; return self; } - insertColAt:(int)col // Keep the storage in synch { MiscColumnSize newCSize, *cSize; if (col < 0) { return nil; } if (col <= numCols) { [super insertColAt:col]; if (col > 0) { cSize = (MiscColumnSize *)[columnSizes elementAt:col]; newCSize.x = cSize->x; } else { newCSize.x = bounds.origin.x; } newCSize.width = cellSize.width; [columnSizes insertElement:&newCSize at:col]; [self _Misc_moveColumnsRightBy:newCSize.width + intercell.width startingAt:col+1]; } else { return nil; } return self; } - insertRowAt:(int)row // Keep the storage in synch { MiscRowSize newRSize, *rSize; if ((row < 0) || (row > numRows)) return nil; if (row == numRows) { if (numRows == 0) newRSize.y = bounds.origin.y; else { rSize = (MiscRowSize *)[rowSizes elementAt:row-1]; newRSize.y = rSize->y + rSize->height + intercell.height; } } else { rSize = (MiscRowSize *)[rowSizes elementAt:row]; newRSize.y = rSize->y; } newRSize.height = cellSize.height; [super insertRowAt:row]; [rowSizes insertElement:&newRSize at:row]; [self _Misc_moveRowsDownBy:newRSize.height + intercell.height startingAt:row+1 ]; return self; } - removeColAt:(int)col andFree:(BOOL)flag // Keep the storage in synch { MiscColumnSize *cSize; NXCoord diff; if ((col >= numCols) || (col < 0)) { return nil; } [super removeColAt:col andFree:flag]; cSize = (MiscColumnSize *)[columnSizes elementAt:col]; diff = cSize->width; [columnSizes removeElementAt:col]; [self _Misc_moveColumnsRightBy:0.0 - diff - intercell.width startingAt:col]; return self; } - removeRowAt:(int)row andFree:(BOOL)flag // Keep the storage in synch { MiscRowSize *rSize; NXCoord diff; if ((row >= numRows) || (row < 0)) { return nil; } [super removeRowAt:row andFree:flag]; rSize = (MiscRowSize *)[rowSizes elementAt:row]; diff = rSize->height; [rowSizes removeElementAt:row]; [self _Misc_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 { MiscColumnSize *cSize; MiscRowSize *rSize; if (col < numCols) { cSize = (MiscColumnSize *)[columnSizes elementAt:col]; theRect->origin.x = cSize->x; theRect->size.width = cSize->width; } else { int num = col - numCols; cSize = (MiscColumnSize *)[columnSizes lastElement]; theRect->origin.x = cSize->x + (num * (cellSize.width + intercell.width)); theRect->size.width = cellSize.width; } if (row < numRows) { rSize = (MiscRowSize *)[rowSizes elementAt:row]; theRect->origin.y = rSize->y; theRect->size.height = rSize->height; } else { int num = row - numRows; rSize = (MiscRowSize *)[rowSizes 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 { MiscColumnSize *cSize; MiscRowSize *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 nil; } for (i=0; i<numCols; i++) { cSize = (MiscColumnSize *)[columnSizes elementAt:i]; if ((aPoint->x >= cSize->x) && (aPoint->x <= cSize->x + cSize->width)) { *col = i; break; } } for (i=0; i<numRows; i++) { rSize = (MiscRowSize *)[rowSizes elementAt:i]; if ((aPoint->y >= rSize->y) && (aPoint->y <= rSize->y + rSize->height)) { *row = i; break; } } return ((*row == -1) || (*col == -1)) ? nil : self; } - setIntercell:(const NXSize *)aSize // Keep the storage in synch { NXCoord xDiff = aSize->width - intercell.width; NXCoord yDiff = aSize->height - intercell.height; MiscRowSize *rSize; MiscColumnSize *cSize; int i; for (i=1; i<numRows; i++) { rSize = (MiscRowSize *)[rowSizes elementAt:i]; rSize->y += (yDiff * i); } for (i=1; i<numCols; i++) { cSize = (MiscColumnSize *)[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; } - sizeRowsToFitCells { NXRect veryBig; NXSize size; NXRect myFrame; NXCoord maxHeight; NXCoord offset = 0.0; MiscRowSize *rSize; int i,j; veryBig.size.width = cellSize.width; veryBig.size.height = MAXFLOAT; [self getFrame:&myFrame]; for (i = 0; i < numRows; i++) { maxHeight = 0.0; rSize = (MiscRowSize *)[rowSizes elementAt:i]; rSize->y = offset; for (j = 0; j < numCols; j++) { [[self cellAt:i :j] calcCellSize:&size inRect:&veryBig]; if (size.height > maxHeight) maxHeight = size.height; } rSize->height = maxHeight; offset += maxHeight + intercell.height; } myFrame.size.height = offset; [self setFrame:&myFrame]; return self; } - sizeTo:(NXCoord)width :(NXCoord)height { NXRect oldFrame; NXCoord dx, dy; float sx, sy; printf ("sizeTo\n"); [self getFrame:&oldFrame]; [super sizeTo:width :height]; dx = width - oldFrame.size.width; dy = height - oldFrame.size.height; if ([self doesAutosizeCells] && (numRows > 0) && (numCols > 0)) { NXCoord offset; int i; MiscRowSize *rSize; MiscColumnSize *cSize; sx = 1 + (dx/(oldFrame.size.width - (numRows - 1)*intercell.width)); sy = 1 + (dy/(oldFrame.size.height - (numCols - 1)*intercell.height)); for (i=0, offset = 0.0; i < numRows; i++) { rSize = (MiscRowSize *)[rowSizes elementAt:i]; rSize->y = offset; rSize->height *= floor(sy+0.5); offset += rSize->height + intercell.height; } for (i=0, offset = 0.0; i < numCols; i++) { cSize = (MiscColumnSize *)[columnSizes elementAt:i]; cSize->x = offset; cSize->width *= sx; offset += cSize->width + intercell.width; } } return self; } @end @implementation Storage(MiscLastElementCategory) - (void *)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.