home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / SourceCode / MiscKit1.2.6 / Source / MiscMatrix.m < prev    next >
Encoding:
Text File  |  1994-03-22  |  14.0 KB  |  632 lines

  1. //
  2. //    MiscMatrix.m -- a class to implement variable-sized matrices
  3. //        Written by Mike Ferris (c) 1994 by Mike Ferris.
  4. //        Modified from original MOKit "MOMatrix" class by Don Yacktman.
  5. //                Version 1.0.  All rights reserved.
  6. //
  7. //        This notice may not be removed from this source code.
  8. //
  9. //    This object is included in the MiscKit by permission from the author
  10. //    and its use is governed by the MiscKit license, found in the file
  11. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  12. //    for a list of all applicable permissions and restrictions.
  13. //    
  14.  
  15. // MiscMatrix is a subclass of Matrix that allows independantly sizable
  16. // rows and columns.  Each row can have a different height and each column
  17. // can have a different width.
  18.  
  19. #import <misckit/MiscMatrix.h>
  20. #import <objc/objc-runtime.h>
  21.  
  22. #define CLASS_VERSION         0
  23. #define CLASS_NAME            "MiscMatrix"
  24.  
  25. // These are the private methods we use in MiscMatrix
  26. @interface MiscMatrix(private)
  27.  
  28. - _Misc_copyRowSizes:rs andColSizes:cs zone:(NXZone *)zone;
  29. - _Misc_moveColumnsRightBy:(NXCoord)difference startingAt:(int)col;
  30. - _Misc_moveRowsDownBy:(NXCoord)difference startingAt:(int)row;
  31.  
  32. @end
  33.  
  34. @implementation MiscMatrix
  35.  
  36. + initialize
  37. // Set the class version
  38. {
  39.     if (self == objc_lookUpClass(CLASS_NAME))  {
  40.         [self setVersion:CLASS_VERSION];
  41.     }
  42.     return self;
  43. }
  44.  
  45. - setupStorage:(int)rowsHigh :(int)colsWide
  46. // Set up our storage objects
  47. {
  48.     int i;
  49.     MiscColumnSize newCSize;
  50.     MiscRowSize newRSize;
  51.     
  52.     columnSizes = [[Storage allocFromZone:[self zone]] initCount:0 
  53.                 elementSize:sizeof(MiscColumnSize)
  54.                 description:MISC_COLUMNSIZE_DESC];
  55.     rowSizes = [[Storage allocFromZone:[self zone]] initCount:0 
  56.                 elementSize:sizeof(MiscRowSize)
  57.                 description:MISC_ROWSIZE_DESC];
  58.     
  59.     newCSize.width = cellSize.width;
  60.     newRSize.height = cellSize.height;
  61.     for (i=0; i<colsWide; i++)  {
  62.         newCSize.x = bounds.origin.x + (i*cellSize.width) + 
  63.                     (i*intercell.width);
  64.         [columnSizes addElement:&newCSize];
  65.     }
  66.     for (i=0; i<rowsHigh; i++)  {
  67.         newRSize.y = bounds.origin.y + (i*cellSize.height) + 
  68.                     (i*intercell.height);
  69.         [rowSizes addElement:&newRSize];
  70.     }
  71.     
  72.     return self;
  73. }
  74.  
  75. - initFrame:(const NXRect *)frm mode:(int)aMode prototype:cellId 
  76.             numRows:(int)rowsHigh numCols:(int)colsWide
  77. // Designated initializer override from Matrix.  Sets up our storage stuff.
  78. {
  79.     [super initFrame:frm mode:aMode prototype:cellId numRows:numRows 
  80.                 numCols:numCols];
  81.     
  82.     [self setupStorage:rowsHigh :colsWide];
  83.     
  84.     return self;
  85. }
  86.  
  87. - initFrame:(const NXRect *)frm mode:(int)aMode cellClass:factoryId 
  88.             numRows:(int)rowsHigh numCols:(int)colsWide
  89. // Designated initializer override from Matrix.  Sets up our storage stuff.
  90. {
  91.     [super initFrame:frm mode:aMode cellClass:factoryId numRows:numRows 
  92.                 numCols:numCols];
  93.     
  94.     [self setupStorage:rowsHigh :colsWide];
  95.     
  96.     return self;
  97. }
  98.  
  99. - _Misc_copyRowSizes:rs andColSizes:cs zone:(NXZone *)zone
  100. {
  101.     rowSizes = [rs copyFromZone:zone];
  102.     columnSizes = [cs copyFromZone:zone];
  103.     return self;
  104. }
  105.  
  106. - copyFromZone:(NXZone *)zone
  107. {
  108.     id obj = [super copyFromZone:zone];
  109.     [obj _Misc_copyRowSizes:rowSizes andColSizes:columnSizes zone:zone];
  110.     return obj;
  111. }
  112.  
  113. - free
  114. // free the storage
  115. {
  116.     [columnSizes free];
  117.     [rowSizes free];
  118.     return [super free];
  119. }
  120.  
  121. - _Misc_moveColumnsRightBy:(NXCoord)difference startingAt:(int)col
  122. // A private method used by the methods which cause sizing stuff to change
  123. {
  124.     int i;
  125.  
  126.     if ((col < 0) || (col >= numCols))  {
  127.         return nil;
  128.     }
  129.  
  130.     for (i=col; i<numCols; i++)  {
  131.         MiscColumnSize *cSize;
  132.         cSize = (MiscColumnSize *)[columnSizes elementAt:i];
  133.         if (cSize)  cSize->x += difference;
  134.     }
  135.     return self;
  136. }
  137.  
  138. - _Misc_moveRowsDownBy:(NXCoord)difference startingAt:(int)row
  139. // A private method used by the methods which cause sizing stuff to change
  140. {
  141.     int i;
  142.     
  143.     if ((row < 0) || (row >= numRows))  {
  144.         return nil;
  145.     }
  146.  
  147.     for (i=row; i<numRows; i++)  {
  148.         MiscRowSize *rSize;
  149.         rSize = (MiscRowSize *)[rowSizes elementAt:i];
  150.         if (rSize)  rSize->y += difference;
  151.     }
  152.     return self;
  153. }
  154.  
  155. - setWidth:(NXCoord)newWidth ofCol:(int)col
  156. // This method allows the setting of column widths
  157. {
  158.     NXCoord diff;
  159.     MiscColumnSize *cSize;
  160.     
  161.     if ((col < 0) || (col >= numCols))  {
  162.         return nil;
  163.     }
  164.  
  165.     cSize = (MiscColumnSize *)[columnSizes elementAt:col];
  166.     diff = newWidth - cSize->width;
  167.     cSize->width = newWidth;
  168.     [self _Misc_moveColumnsRightBy:diff startingAt:col+1];
  169.     
  170.     return self;
  171. }
  172.  
  173. - setHeight:(NXCoord)newHeight ofRow:(int)row
  174. // This method allows the setting of row heights
  175. {
  176.     NXCoord diff;
  177.     MiscRowSize *rSize;
  178.     
  179.     if ((row < 0) || (row >= numRows))  {
  180.         return nil;
  181.     }
  182.  
  183.     rSize = (MiscRowSize *)[rowSizes elementAt:row];
  184.     diff = newHeight - rSize->height;
  185.     rSize->height = newHeight;
  186.     [self _Misc_moveRowsDownBy:diff startingAt:row+1];
  187.         
  188.     return self;
  189. }
  190.  
  191. - sizeToCells
  192. // Resize the matrix to the proper size to fit all our cells.
  193. {
  194.     NXRect rect;
  195.     MiscColumnSize *cSize;
  196.     MiscRowSize *rSize;
  197.     
  198.     [self getFrame:&rect];
  199.     
  200.     if (numCols == 0)  {
  201.         rect.size.width = 0.0;
  202.     }  else  {
  203.         cSize = (MiscColumnSize *)[columnSizes lastElement];
  204.         rect.size.width = cSize->x + cSize->width - bounds.origin.x;
  205.     }
  206.  
  207.     if (numRows == 0)  {
  208.         rect.size.height = 0.0;
  209.     }  else  {
  210.         rSize = (MiscRowSize *)[rowSizes lastElement];
  211.         rect.size.height = rSize->y + rSize->height - bounds.origin.y;
  212.     }
  213.     
  214.     [self setFrame:&rect];
  215.  
  216.     return self;
  217. }
  218.  
  219. - renewRows:(int)newRows cols:(int)newCols
  220. // Makes sure to keep our storage objects in synch with everything else.
  221. {
  222.     MiscColumnSize newCSize, *cSize;
  223.     MiscRowSize newRSize, *rSize;
  224.     int i;
  225.     
  226.     // Remove any storage elements past the new number of cols
  227.     for (i=numCols-1; i>=newCols; i--)  {
  228.         [columnSizes removeLastElement];
  229.     }
  230.     // Add any needed new storage elements to get up to the new number of cols
  231.     for (i=numCols; i<newCols; i++)  {
  232.         if (i==0)  {
  233.             newCSize.x = bounds.origin.x;
  234.         }  else  {
  235.             cSize = (MiscColumnSize *)[columnSizes lastElement];
  236.             newCSize.x = cSize->x + cSize->width + intercell.width;
  237.         }
  238.         newCSize.width = cellSize.width;
  239.         [columnSizes addElement:&newCSize];
  240.     }
  241.  
  242.     // Remove any storage elements past the new number of rows
  243.     for (i=numRows-1; i>=newRows; i++)  {
  244.         [rowSizes removeLastElement];
  245.     }
  246.     // Add any needed new storage elements to get up to the new number of rows
  247.     for (i=numRows; i<newRows; i++)  {
  248.         if (i==0)  {
  249.             newRSize.y = bounds.origin.y;
  250.         }  else  {
  251.             rSize = (MiscRowSize *)[rowSizes lastElement];
  252.             newRSize.y = rSize->y + rSize->height + intercell.height;
  253.         }
  254.         newRSize.height = cellSize.height;
  255.         [rowSizes addElement:&newRSize];
  256.     }
  257.     
  258.     [super renewRows:newRows cols:newCols];
  259.     
  260.     return self;
  261. }
  262.  
  263. - addCol
  264. // Keep the storage in synch
  265. {
  266.     MiscColumnSize newCSize, *cSize;
  267.     
  268.     [super addCol];
  269.  
  270.     if (numCols > 0)  {
  271.         cSize = (MiscColumnSize *)[columnSizes lastElement];
  272.         newCSize.x = cSize->x + cSize->width + intercell.width;
  273.     }  else  {
  274.         newCSize.x = bounds.origin.x;
  275.     }
  276.     newCSize.width = cellSize.width;
  277.     [columnSizes addElement:&newCSize];
  278.     
  279.     return self;
  280. }
  281.  
  282. - addRow
  283. // Keep the storage in synch
  284. {
  285.     MiscRowSize newRSize, *rSize;
  286.     
  287.     [super addRow];
  288.  
  289.     if (numRows > 0)  {
  290.         rSize = (MiscRowSize *)[rowSizes lastElement];
  291.         newRSize.y = rSize->y + rSize->height + intercell.height;
  292.     }  else  {
  293.         newRSize.y = bounds.origin.y;
  294.     }
  295.     newRSize.height = cellSize.height;
  296.     [rowSizes addElement:&newRSize];
  297.     
  298.     return self;
  299. }
  300.  
  301. - insertColAt:(int)col
  302. // Keep the storage in synch
  303. {
  304.     MiscColumnSize newCSize, *cSize;
  305.     
  306.     if (col < 0)  {
  307.         return nil;
  308.     }
  309.     
  310.     if (col <= numCols)  {
  311.         [super insertColAt:col];
  312.  
  313.         if (col > 0)  {
  314.             cSize = (MiscColumnSize *)[columnSizes elementAt:col];
  315.             newCSize.x = cSize->x;
  316.         }  else  {
  317.             newCSize.x = bounds.origin.x;
  318.         }
  319.         newCSize.width = cellSize.width;
  320.         [columnSizes insertElement:&newCSize at:col];
  321.         
  322.         [self _Misc_moveColumnsRightBy:newCSize.width + intercell.width 
  323.                     startingAt:col+1];
  324.     }  else  {
  325.         return nil;
  326.     }
  327.     return self;
  328. }
  329.  
  330. - insertRowAt:(int)row
  331. // Keep the storage in synch
  332. {
  333.     MiscRowSize newRSize, *rSize;
  334.     
  335.     if (row < 0)  {
  336.         return nil;
  337.     }
  338.     
  339.     if (row <= numRows)  {
  340.         [super insertRowAt:row];
  341.  
  342.         if (row > 0)  {
  343.             rSize = (MiscRowSize *)[rowSizes elementAt:row];
  344.             newRSize.y = rSize->y;
  345.         }  else  {
  346.             newRSize.y = bounds.origin.y;
  347.         }
  348.         newRSize.height = cellSize.height;
  349.         [rowSizes insertElement:&newRSize at:row+1];
  350.         
  351.         [self _Misc_moveRowsDownBy:newRSize.height + intercell.height 
  352.                     startingAt:row+1];
  353.     }  else  {
  354.         return nil;
  355.     }
  356.     return self;
  357. }
  358.  
  359. - removeColAt:(int)col andFree:(BOOL)flag
  360. // Keep the storage in synch
  361. {
  362.     MiscColumnSize *cSize;
  363.     NXCoord diff;
  364.     
  365.     if ((col >= numCols) || (col < 0))  {
  366.         return nil;
  367.     }
  368.     
  369.     [super removeColAt:col andFree:flag];
  370.  
  371.     cSize = (MiscColumnSize *)[columnSizes elementAt:col];
  372.     diff = cSize->width;
  373.     [columnSizes removeElementAt:col];
  374.     [self _Misc_moveColumnsRightBy:0.0 - diff - intercell.width
  375.             startingAt:col];
  376.     
  377.     return self;
  378. }
  379.  
  380. - removeRowAt:(int)row andFree:(BOOL)flag
  381. // Keep the storage in synch
  382. {
  383.     MiscRowSize *rSize;
  384.     NXCoord diff;
  385.     
  386.     if ((row >= numRows) || (row < 0))  {
  387.         return nil;
  388.     }
  389.  
  390.     [super removeRowAt:row andFree:flag];
  391.  
  392.     rSize = (MiscRowSize *)[rowSizes elementAt:row];
  393.     diff = rSize->height;
  394.     [rowSizes removeElementAt:row];
  395.     [self _Misc_moveRowsDownBy:0.0 - diff - intercell.height startingAt:row];
  396.     
  397.     return self;
  398. }
  399.  
  400. - drawSelf:(const NXRect *)rects:(int)rectCount
  401. // We do our own drawing because we need to draw our cells in diverse 
  402. // rectangles
  403. {
  404.     int i, j;
  405.     NXRect cFrm;
  406.     
  407.     [window disableFlushWindow];
  408.     
  409.     // the background (if any)
  410.     if (backgroundGray != -1.0)  {
  411.         PSsetgray(backgroundGray);
  412.         if (rectCount==1)  {
  413.             NXRectFill(&(rects[0]));
  414.         }  else  {
  415.             NXRectFill(&(rects[1]));
  416.             NXRectFill(&(rects[2]));
  417.         }
  418.     }
  419.     
  420.     // The cells
  421.     for (i=0; i<numRows; i++)  {
  422.         for (j=0; j<numCols; j++)  {
  423.             [self getCellFrame:&cFrm at:i:j];
  424.             if (rectCount == 1)  {
  425.                 if (NXIntersectsRect(&(rects[0]), &cFrm))  {
  426.                     [self drawCellAt:i:j];
  427.                 }
  428.             }  else  {
  429.                 if ((NXIntersectsRect(&(rects[1]), &cFrm)) || 
  430.                             (NXIntersectsRect(&(rects[2]), &cFrm)))  {
  431.                     [self drawCellAt:i:j];
  432.                 }
  433.             }
  434.         }
  435.     }
  436.  
  437.     [window reenableFlushWindow];
  438.     [window flushWindow];
  439.     
  440.     return self;
  441. }
  442.  
  443. - getCellFrame:(NXRect *)theRect at:(int)row:(int)col
  444. // Calculate and return the rect used to display the cell at the given
  445. // row and column
  446. {
  447.     MiscColumnSize *cSize;
  448.     MiscRowSize *rSize;
  449.     
  450.     if (col < numCols)  {
  451.         cSize = (MiscColumnSize *)[columnSizes elementAt:col];
  452.         theRect->origin.x = cSize->x;
  453.         theRect->size.width = cSize->width;
  454.     }  else  {
  455.         int num = col - numCols;
  456.         
  457.         cSize = (MiscColumnSize *)[columnSizes lastElement];
  458.         theRect->origin.x = cSize->x +
  459.                     (num * (cellSize.width + intercell.width));
  460.         theRect->size.width = cellSize.width;
  461.     }
  462.     
  463.     if (row < numRows)  {
  464.         rSize = (MiscRowSize *)[rowSizes elementAt:row];
  465.         theRect->origin.y = rSize->y;
  466.         theRect->size.height = rSize->height;
  467.     }  else  {
  468.         int num = row - numRows;
  469.         
  470.         rSize = (MiscRowSize *)[rowSizes lastElement];
  471.         theRect->origin.y = rSize->y +
  472.                     (num * (cellSize.height + intercell.height));
  473.         theRect->size.height = cellSize.height;
  474.     }
  475.     
  476.     return self;
  477. }
  478.  
  479. - getRow:(int *)row andCol:(int *)col forPoint:(const NXPoint *)aPoint
  480. // Calculate the row and column of the cell which contains the given point
  481. {
  482.     MiscColumnSize *cSize;
  483.     MiscRowSize *rSize;
  484.     int i;
  485.     
  486.     *row = -1;
  487.     *col = -1;
  488.     if ((aPoint->x < bounds.origin.x) || 
  489.                 (aPoint->x > bounds.origin.x + bounds.size.width) || 
  490.                 (aPoint->y < bounds.origin.y) || 
  491.                 (aPoint->y > bounds.origin.y + bounds.size.height))  {
  492.         return self;
  493.     }
  494.     for (i=0; i<numCols; i++)  {
  495.         cSize = (MiscColumnSize *)[columnSizes elementAt:i];
  496.         if ((aPoint->x >= cSize->x) && 
  497.                     (aPoint->x <= cSize->x + cSize->width))  {
  498.             *col = i;
  499.             break;
  500.         }
  501.     }
  502.     for (i=0; i<numRows; i++)  {
  503.         rSize = (MiscRowSize *)[rowSizes elementAt:i];
  504.         if ((aPoint->y >= rSize->y) && 
  505.                     (aPoint->y <= rSize->y + rSize->height))  {
  506.             *row = i;
  507.             break;
  508.         }
  509.     }
  510.     return self;
  511. }
  512.  
  513. - setIntercell:(const NXSize *)aSize
  514. // Keep the storage in synch
  515. {
  516.     NXCoord xDiff = aSize->width - intercell.width;
  517.     NXCoord yDiff = aSize->height - intercell.height;
  518.     MiscRowSize *rSize;
  519.     MiscColumnSize *cSize;
  520.     int i;
  521.     
  522.     for (i=1; i<numRows; i++)  {
  523.         rSize = (MiscRowSize *)[rowSizes elementAt:i];
  524.         rSize->y += (yDiff * i);
  525.     }
  526.     for (i=1; i<numCols; i++)  {
  527.         cSize = (MiscColumnSize *)[columnSizes elementAt:i];
  528.         cSize->x += (xDiff * i);
  529.     }
  530.     
  531.     return [super setIntercell:aSize];
  532. }
  533.  
  534.  
  535. - write:(NXTypedStream *)typedStream
  536. // Write our ivars
  537. {
  538.     [super write:typedStream];
  539.     NXWriteObject(typedStream, columnSizes);
  540.     NXWriteObject(typedStream, rowSizes);
  541.     return self;
  542. }
  543.  
  544. - read:(NXTypedStream *)typedStream
  545. // Read our ivars
  546. {
  547.     int classVersion;
  548.  
  549.     [super read:typedStream];
  550.     
  551.     classVersion = NXTypedStreamClassVersion(typedStream, CLASS_NAME);
  552.     
  553.     switch (classVersion)  {
  554.         case 0:        // First version.
  555.             columnSizes = NXReadObject(typedStream);
  556.             rowSizes = NXReadObject(typedStream);
  557.             break;
  558.         default:
  559.             NXLogError("[%s read:] class version %d cannot read "
  560.                         "instances archived with version %d", 
  561.                         CLASS_NAME, CLASS_VERSION, classVersion);
  562.             [self setupStorage:numRows :numCols];
  563.             break;
  564.     }
  565.     return self;
  566. }
  567.  
  568.  
  569. // ********************Overridden private methods***********************
  570. // *****************that I'm going to hell for using********************
  571.  
  572. // These methods are used by Matrix's mouseDown:.  Doing the whole 
  573. // mouseDown: method over would have been a royal pain in the butt, 
  574. // so I cheated.
  575.  
  576.  
  577. - (BOOL)_mouseHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
  578. {
  579.     NXPoint    point;
  580.     id        ret;
  581.  
  582.     point = *forpoint;
  583.     [self convertPoint:&point fromView:nil];
  584.     ret = [self getRow:row andCol:col forPoint:&point];
  585.  
  586.     if (ret == nil)
  587.         return NO;
  588.     return YES;
  589. }
  590.  
  591. - (BOOL)_loopHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
  592. {
  593.     NXPoint    point;
  594.     id        ret;
  595.  
  596.     point = *forpoint;
  597.     [self convertPoint:&point fromView:nil];
  598.     ret = [self getRow:row andCol:col forPoint:&point];
  599.  
  600.     if (ret == nil)
  601.         return NO;
  602.     return YES;
  603. }
  604.  
  605. - (BOOL)_radioHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
  606. {
  607.     NXPoint    point;
  608.     id        ret;
  609.  
  610.     point = *forpoint;
  611.     [self convertPoint:&point fromView:nil];
  612.     ret = [self getRow:row andCol:col forPoint:&point];
  613.  
  614.     if (ret == nil)
  615.         return NO;
  616.     return YES;
  617. }
  618.  
  619.  
  620. @end
  621.  
  622. @implementation Storage(MiscLastElementCategory)
  623.     
  624. - (void *)lastElement
  625. // A little shortcut
  626. {
  627.     return [self elementAt:numElements-1];
  628. }
  629.     
  630. @end
  631.  
  632.