home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / YellowBox / Kits / MiscTableScroll-138.1 / Palettes / MiscTableScroll / Framework / MiscTableView.M < prev    next >
Encoding:
Text File  |  1998-03-31  |  32.8 KB  |  1,048 lines

  1. //=============================================================================
  2. //
  3. //  Copyright (C) 1995,1996,1997,1998 by Paul S. McCarthy and Eric Sunshine.
  4. //        Written by Paul S. McCarthy and Eric Sunshine.
  5. //                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 authors
  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. //-----------------------------------------------------------------------------
  16. // MiscTableView.M
  17. //
  18. //    General-purpose 2-D display object that works with the
  19. //    MiscTableScroll to provide row/column sizing and dragging.
  20. //
  21. //    This object is responsible for drawing, mouse and keyboard
  22. //    events in the content portion of the display.
  23. //
  24. //-----------------------------------------------------------------------------
  25. //-----------------------------------------------------------------------------
  26. // $Id: MiscTableView.M,v 1.38 98/03/29 23:58:32 sunshine Exp $
  27. // $Log:    MiscTableView.M,v $
  28. //  Revision 1.38  98/03/29  23:58:32  sunshine
  29. //  v138.1: Now uses NSColor's "system" color for grid rather than "gray".
  30. //  
  31. //  Revision 1.37  98/03/22  13:13:33  sunshine
  32. //  v133.1: Added support to draw clipped text.
  33. //  
  34. //  Revision 1.36  97/11/23  07:41:07  sunshine
  35. //  v130.1: Broke off MiscTableViewDrag.M and MiscTableViewCursor.M
  36. //-----------------------------------------------------------------------------
  37. #import "MiscTableViewPrivate.h"
  38. #import    "MiscColorList.h"
  39. #import    "MiscDrawList.h"
  40. #import "MiscGeometry.h"
  41. #import "MiscHighlightTracker.h"
  42. #import "MiscListTracker.h"
  43. #import "MiscRadioTracker.h"
  44. #import    "MiscRectColorList.h"
  45. #import    "MiscRectList.h"
  46. #import "MiscSparseSet.h"
  47. #import "MiscTableBorder.h"
  48. #import "MiscTableScrollPrivate.h"
  49. #import <MiscTableScroll/MiscTableCell.h>
  50. #import <MiscTableScroll/MiscTableScroll.h>
  51. #import    <new.h>
  52.  
  53. extern "Objective-C" {
  54. #import <AppKit/NSApplication.h>
  55. #import <AppKit/NSControl.h>    // Control-text notifications
  56. #import <AppKit/NSText.h>
  57. #import <AppKit/psops.h>
  58. }
  59.  
  60. extern "C" {
  61. #import <math.h>    // floor()
  62. }
  63.  
  64. //-----------------------------------------------------------------------------
  65. // dump_map
  66. //-----------------------------------------------------------------------------
  67. // static void dump_map( NSString* label, int const* map, int nc, int nr )
  68. //     {
  69. //     fprintf( stderr, "%s nc=%d, nr=%d\n", [label lossyCString], nc, nr );
  70. //     int const* p = map;
  71. //     for (int r = 0; r < nr; r++)
  72. //         {
  73. //         for (int c = 0; c < nc; c++, p++)
  74. //             fprintf( stderr, " %d", *p );
  75. //         fprintf( stderr, "\n" );
  76. //         }
  77. //     }
  78.  
  79.  
  80. //-----------------------------------------------------------------------------
  81. // extract_rect
  82. //    Extract a contiguous rectangular group of cells to be drawn from
  83. //    this boolean map of cells that need drawing.
  84. //
  85. //    1) Find the first cell that needs to be drawn (c0,r0).
  86. //    2) Include all contiguous following cells on the same row
  87. //        that also need to be drawn (cN,r0).
  88. //    3) Include all contiguous following rows in which (c0..cN)
  89. //        all need to be drawn.
  90. //    4) Clear the flags from the map to indicate that these cells
  91. //        do not need to be drawn again.
  92. //-----------------------------------------------------------------------------
  93. static int extract_rect( int* map, int nc, int nr,
  94.             MiscCoord_V& c0, MiscCoord_V& r0,
  95.             MiscCoord_V& cN, MiscCoord_V& rN )
  96.     {
  97.     int* p = map;
  98.     int* plim = p + (nc * nr);
  99.  
  100.     while (p < plim && *p == 0)
  101.     p++;
  102.  
  103.     if (p >= plim)
  104.     return 0;            // *** RETURN ***
  105.  
  106.     int rc = *p;
  107.  
  108.     int r, c;
  109.  
  110.     int const n = p - map;
  111.     r0 = n / nc;
  112.     c0 = n % nc;
  113.  
  114.     c = c0;
  115.     while (c < nc && *p == rc)
  116.     {
  117.     c++;
  118.     p++;
  119.     }
  120.     cN = c - 1;
  121.  
  122.     int const num_cols = (c - c0);
  123.  
  124.     r = r0;
  125.     do  {
  126.     p -= num_cols;
  127.     memset( p, 0, num_cols * sizeof(*p) );
  128.     if (++r >= nr)
  129.         break;
  130.     p += nc;
  131.     plim = p + num_cols;
  132.     while (p < plim && *p == rc)
  133.         p++;
  134.     }
  135.     while (p >= plim);
  136.  
  137.     rN = r - 1;
  138.  
  139.     return rc;
  140.     }
  141.  
  142.  
  143. //=============================================================================
  144. // IMPLEMENTATION
  145. //=============================================================================
  146. @implementation MiscTableView
  147.  
  148. //-----------------------------------------------------------------------------
  149. // scroll
  150. //-----------------------------------------------------------------------------
  151. - (id)scroll
  152.     {
  153.     return [self enclosingScrollView];
  154.     }
  155.  
  156.  
  157. //-----------------------------------------------------------------------------
  158. // - initWithFrame:scroll:colInfo:rowInfo:
  159. //
  160. //    NOTE *1*: Default behavior is to take keyboard-cursor information from
  161. //        the row-border.
  162. //-----------------------------------------------------------------------------
  163. - (id)initWithFrame:(NSRect)rect
  164.     scroll:(MiscTableScroll*)scroll
  165.     colInfo:(MiscTableBorder*)i_col_border
  166.     rowInfo:(MiscTableBorder*)i_row_border
  167.     {
  168.     NSZone* const z = [self zone];
  169.  
  170.     rect.size.width = i_col_border->totalSize();
  171.     rect.size.height = i_row_border->totalSize();
  172.  
  173.     [super initWithFrame:rect];
  174.  
  175.     colBorder = i_col_border;
  176.     rowBorder = i_row_border;
  177.     trackerBorder = MISC_ROW_BORDER;            // NOTE *1*
  178.  
  179.     oldColSel = new( NSZoneMalloc(z,sizeof(*oldColSel)) ) MiscSparseSet;
  180.     oldRowSel = new( NSZoneMalloc(z,sizeof(*oldRowSel)) ) MiscSparseSet;
  181.     [self setSelectionMode:[scroll selectionMode]];
  182.  
  183.     inhibitCursor = 0;
  184.     cursorSlot = -1;
  185.     return self;
  186.     }
  187.  
  188.  
  189. //-----------------------------------------------------------------------------
  190. // - dealloc
  191. //-----------------------------------------------------------------------------
  192. - (void)dealloc
  193.     {
  194.     [[self scroll] abortEditing];
  195.     [tracker release];
  196.     NSZone* const z = [self zone];
  197.     if (oldColSel != 0)
  198.     {
  199.     oldColSel->MiscSparseSet::~MiscSparseSet();
  200.     NSZoneFree( z, oldColSel );
  201.     }
  202.     if (oldRowSel != 0)
  203.     {
  204.     oldRowSel->MiscSparseSet::~MiscSparseSet();
  205.     NSZoneFree( z, oldRowSel );
  206.     }
  207.     [super dealloc];
  208.     }
  209.  
  210.  
  211. //-----------------------------------------------------------------------------
  212. // - isFlipped
  213. //-----------------------------------------------------------------------------
  214. - (BOOL)isFlipped
  215.     {
  216.     return YES;
  217.     }
  218.  
  219.  
  220. //-----------------------------------------------------------------------------
  221. // - isOpaque
  222. //-----------------------------------------------------------------------------
  223. - (BOOL)isOpaque
  224.     {
  225.     return YES;
  226.     }
  227.  
  228.  
  229. //-----------------------------------------------------------------------------
  230. // - acceptsFirstMouse:
  231. //-----------------------------------------------------------------------------
  232. - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent
  233.     {
  234.     return YES;
  235.     }
  236.  
  237.  
  238. //-----------------------------------------------------------------------------
  239. // - acceptsFirstResponder
  240. //-----------------------------------------------------------------------------
  241. - (BOOL)acceptsFirstResponder
  242.     {
  243.     return YES;
  244.     }
  245.  
  246.  
  247. //-----------------------------------------------------------------------------
  248. // - adjustSize
  249. //-----------------------------------------------------------------------------
  250. - (void)adjustSize
  251.     {
  252.     [self setFrameSize:
  253.         NSMakeSize( colBorder->totalSize(), rowBorder->totalSize() )];
  254.     }
  255.  
  256.  
  257. //=============================================================================
  258. // TYPE VARIATIONS
  259. //=============================================================================
  260.  
  261. - (MiscTableBorder*)borderFor:(MiscBorderType)b
  262.     { return (b == MISC_ROW_BORDER ? rowBorder : colBorder); }
  263. - (MiscTableBorder*)otherBorder:(MiscBorderType)b
  264.     { return [self borderFor:MISC_OTHER_BORDER(b)]; }
  265.  
  266.  
  267. //=============================================================================
  268. // FRAMES
  269. //=============================================================================
  270. //-----------------------------------------------------------------------------
  271. // - cellFrameAtRow:column: -- Physical coords
  272. //-----------------------------------------------------------------------------
  273. - (NSRect)cellFrameAtRow:(MiscCoord_P)row column:(MiscCoord_P)col
  274.     {
  275.     if (row < 0 || row >= rowBorder->count() ||
  276.     col < 0 || col >= colBorder->count())
  277.     return NSMakeRect( 0, 0, 0, 0 );
  278.     else
  279.     {
  280.     MiscCoord_V v_row = rowBorder->physicalToVisual( row );
  281.     MiscCoord_V v_col = colBorder->physicalToVisual( col );
  282.     return NSMakeRect(  colBorder->getOffset(v_col),
  283.                 rowBorder->getOffset(v_row),
  284.                 colBorder->effectiveSize(v_col),
  285.                 rowBorder->effectiveSize(v_row) );
  286.     }
  287.     }
  288.  
  289.  
  290. //-----------------------------------------------------------------------------
  291. // - cellInsideAtRow:column: -- Actual cell frame inside border lines
  292. //-----------------------------------------------------------------------------
  293. - (NSRect)cellInsideAtRow:(MiscCoord_P)row column:(MiscCoord_P)col
  294.     {
  295.     NSRect r = [self cellFrameAtRow:row column:col];
  296.     if (!NSIsEmptyRect(r))
  297.     {
  298.     r.size.width--;
  299.     r.size.height--;
  300.     }
  301.     return r;
  302.     }
  303.  
  304.  
  305. //-----------------------------------------------------------------------------
  306. // - getSlotFrameAt:from: -- Physical coords
  307. //-----------------------------------------------------------------------------
  308. - (NSRect)getSlotFrameAt:(MiscCoord_P)pslot from:(MiscBorderType)bdr
  309.     {
  310.     MiscTableBorder* const b = [self borderFor:bdr];
  311.     if (pslot < 0 || pslot >= b->count())
  312.     return NSZeroRect;
  313.     else
  314.     {
  315.     MiscCoord_V vslot = b->physicalToVisual( pslot );
  316.     MiscRect_O ro( bdr, [self bounds] );
  317.     ro.setX_O( b->getOffset(vslot) );
  318.     ro.setWidth_O( b->effectiveSize(vslot) );
  319.     return (NSRect)ro;
  320.     }
  321.     }
  322.  
  323.  
  324. //-----------------------------------------------------------------------------
  325. // - getSlotInsideAt:from: -- Physical coords
  326. //-----------------------------------------------------------------------------
  327. - (NSRect)getSlotInsideAt:(MiscCoord_P)slot from:(MiscBorderType)bdr
  328.     {
  329.     NSRect r = [self getSlotFrameAt:slot from:bdr];
  330.     if (!NSIsEmptyRect(r))
  331.     {
  332.     r.size.width--;
  333.     r.size.height--;
  334.     }
  335.     return r;
  336.     }
  337.  
  338.  
  339. //-----------------------------------------------------------------------------
  340. // - getRow:column:forPoint:
  341. //-----------------------------------------------------------------------------
  342. - (BOOL)getRow:(int*)row column:(int*)col forPoint:(NSPoint)pt
  343.     {
  344.     if (NSPointInRect( pt, [self bounds] ))
  345.     {
  346.     MiscCoord_V const vr = rowBorder->visualForOffset( (MiscPixels)pt.y );
  347.     MiscCoord_V const vc = colBorder->visualForOffset( (MiscPixels)pt.x );
  348.     *row = rowBorder->visualToPhysical( vr );
  349.     *col = colBorder->visualToPhysical( vc );
  350.     return YES;
  351.     }
  352.     return NO;
  353.     }
  354.  
  355.  
  356. //=============================================================================
  357. // VISIBLE / SCROLLING STUFF
  358. //=============================================================================
  359. //-----------------------------------------------------------------------------
  360. // - firstVisibleSlot: -- Physical coord
  361. //-----------------------------------------------------------------------------
  362. - (int)firstVisibleSlot:(MiscBorderType)bdr
  363.     {
  364.     MiscCoord_P ret = -1;
  365.     MiscTableBorder* const b = [self borderFor:bdr];
  366.     int const lim = b->count();
  367.     if (lim > 0)
  368.     {
  369.     MiscRect_O r( bdr, [self visibleRect] );
  370.     MiscPixels const vorg = r.getX_O();
  371.     MiscPixels const vlim = r.getMaxX_O();
  372.     MiscCoord_V v = b->visualForOffset( vorg );
  373.     if (v < lim - 1 && b->getOffset(v) < vorg && b->getOffset(v+1) < vlim)
  374.         v++;        // First slot whose leading edge is visible
  375.     ret = b->visualToPhysical( v );
  376.     }
  377.     return ret;
  378.     }
  379.  
  380.  
  381. //-----------------------------------------------------------------------------
  382. // - lastVisibleSlot: -- Physical coord
  383. //-----------------------------------------------------------------------------
  384. - (int)lastVisibleSlot:(MiscBorderType)bdr
  385.     {
  386.     MiscCoord_P ret = -1;
  387.     MiscTableBorder* const b = [self borderFor:bdr];
  388.     if (b->count() > 0)
  389.     {
  390.     MiscRect_O r( bdr, [self visibleRect] );
  391.     MiscPixels const vorg = r.getX_O();
  392.     MiscPixels const vlim = r.getMaxX_O();
  393.     MiscCoord_V v = b->visualForOffset( vlim );
  394.     if (v > 0 && b->getOffset(v) + b->effectiveSize(v) > vlim &&
  395.              b->getOffset(v-1) + b->effectiveSize(v-1) > vorg)
  396.         v--;        // Last slot whose trailing edge is visible
  397.     ret = b->visualToPhysical( v );
  398.     }
  399.     return ret;
  400.     }
  401.  
  402.  
  403. //-----------------------------------------------------------------------------
  404. // - numberOfVisibleSlots:
  405. //-----------------------------------------------------------------------------
  406. - (int)numberOfVisibleSlots:(MiscBorderType)bdr
  407.     {
  408.     MiscTableBorder* const b = [self borderFor:bdr];
  409.     if (b->count() > 0)
  410.     {
  411.     MiscRect_O r( bdr, [self visibleRect] );
  412.     MiscPixels const vorg = r.getX_O();
  413.     MiscPixels const vlim = r.getMaxX_O();
  414.     MiscCoord_V const first = b->visualForOffset( vorg );
  415.     MiscCoord_V const last  = b->visualForOffset( vlim );
  416.     return last - first + 1;
  417.     }
  418.     return 0;
  419.     }
  420.  
  421.  
  422. //-----------------------------------------------------------------------------
  423. // - border:slotIsVisible: -- Physical coord
  424. //-----------------------------------------------------------------------------
  425. - (BOOL)border:(MiscBorderType)bdr slotIsVisible:(int)n
  426.     {
  427.     MiscTableBorder* const b = [self borderFor:bdr];
  428.     if (b->count() > 0)
  429.     {
  430.     MiscCoord_V const x = b->physicalToVisual(n);
  431.     MiscPixels const xorg = b->getOffset(x);
  432.     MiscPixels const xlim = xorg + b->effectiveSize(x);
  433.     MiscRect_O r( bdr, [self visibleRect] );
  434.     MiscPixels const vorg = r.getX_O();
  435.     MiscPixels const vlim = r.getMaxX_O();
  436.     return xorg <= vlim && xlim >= vorg;
  437.     }
  438.     return NO;
  439.     }
  440.  
  441.  
  442. //-----------------------------------------------------------------------------
  443. // - border:setFirstVisibleSlot: -- Physical coord
  444. //-----------------------------------------------------------------------------
  445. - (void)border:(MiscBorderType)bdr setFirstVisibleSlot:(int)n
  446.     {
  447.     MiscTableBorder* const b = [self borderFor:bdr];
  448.     int const num_slots = b->count();
  449.     if (0 <= n && n < num_slots)
  450.     {
  451.     MiscCoord_V const x = b->physicalToVisual(n);
  452.     MiscPixels const xorg = b->getOffset(x);
  453.     MiscRect_O r( bdr, [[self superview] bounds] );
  454.     r.setX_O( xorg );
  455.     [self scrollPoint:r];
  456.     }
  457.     }
  458.  
  459.  
  460. //-----------------------------------------------------------------------------
  461. // - border:setLastVisibleSlot: -- Physical coord
  462. //-----------------------------------------------------------------------------
  463. - (void)border:(MiscBorderType)bdr setLastVisibleSlot:(int)n
  464.     {
  465.     MiscTableBorder* const b = [self borderFor:bdr];
  466.     int const num_slots = b->count();
  467.     if (0 <= n && n < num_slots)
  468.     {
  469.     MiscPixels const vwidth =
  470.             MiscRect_O( bdr, [self visibleRect] ).getWidth_O();
  471.     MiscCoord_V const x = b->physicalToVisual(n);
  472.     MiscPixels const vorg = b->getOffset(x) + b->effectiveSize(x) - vwidth;
  473.     MiscRect_O r( bdr, [[self superview] bounds] );
  474.     r.setX_O( vorg );
  475.     [self scrollPoint:r];
  476.     }
  477.     }
  478.  
  479.  
  480. //=============================================================================
  481. // SCROLLING
  482. //=============================================================================
  483. //-----------------------------------------------------------------------------
  484. // - scrollCellToVisibleAtRow:column: -- Physical coords
  485. //-----------------------------------------------------------------------------
  486. - (void)scrollCellToVisibleAtRow:(int)row column:(int)col
  487.     {
  488.     [self scrollRectToVisible:[self cellFrameAtRow:row column:col]];
  489.     }
  490.  
  491.  
  492. //-----------------------------------------------------------------------------
  493. // - border:scrollToVisible: -- Physical coord
  494. //-----------------------------------------------------------------------------
  495. - (void)border:(MiscBorderType)bdr scrollToVisible:(MiscCoord_P)pslot
  496.     {
  497.     MiscTableBorder* const b = [self borderFor:bdr];
  498.     MiscCoord_V const vslot = b->physicalToVisual( pslot );
  499.     NSRect v = [self visibleRect];
  500.     MiscRect_O r( bdr, v );
  501.     r.setX_O( b->getOffset(vslot) );
  502.     r.setWidth_O( b->effectiveSize(vslot) );
  503.     [self scrollRectToVisible:r];
  504.     }
  505.  
  506.  
  507. //-----------------------------------------------------------------------------
  508. // - scrollRowToVisible: -- Physical coord
  509. //-----------------------------------------------------------------------------
  510. - (void)scrollRowToVisible:(int)row
  511.     {
  512.     [self border:MISC_ROW_BORDER scrollToVisible:row];
  513.     }
  514.  
  515.  
  516. //-----------------------------------------------------------------------------
  517. // - scrollColToVisible: -- Physical coord
  518. //-----------------------------------------------------------------------------
  519. - (void)scrollColumnToVisible:(int)col
  520.     {
  521.     [self border:MISC_COL_BORDER scrollToVisible:col];
  522.     }
  523.  
  524.  
  525. //=============================================================================
  526. // DRAWING
  527. //=============================================================================
  528. //-----------------------------------------------------------------------------
  529. // - cMin:cMax:rMin:rMax:forRect:
  530. //-----------------------------------------------------------------------------
  531. - (void)cMin:(MiscCoord_V*)cmin cMax:(MiscCoord_V*)cmax
  532.      rMin:(MiscCoord_V*)rmin rMax:(MiscCoord_V*)rmax
  533.      forRect:(NSRect)nsrect
  534.     {
  535.     *cmin = colBorder->visualForOffset( (MiscPixels) nsrect.origin.x );
  536.     *cmax = colBorder->visualForOffset( 
  537.         (MiscPixels) (nsrect.origin.x + nsrect.size.width) - 1 );
  538.     *rmin = rowBorder->visualForOffset( (MiscPixels) nsrect.origin.y );
  539.     *rmax = rowBorder->visualForOffset(
  540.         (MiscPixels) (nsrect.origin.y + nsrect.size.height) - 1 );
  541.     }
  542.  
  543.  
  544. //-----------------------------------------------------------------------------
  545. // - drawRect:
  546. //-----------------------------------------------------------------------------
  547. - (void)drawRect:(NSRect)nsrect
  548.     {
  549.     BOOL const need_clip = ([[self subviews] count] != 0);
  550.     if (need_clip)
  551.     {
  552.     PSgsave();
  553.     NSRectClip( nsrect );
  554.     }
  555.  
  556.     MiscCoord_V c, cmin, cmax, c0, cN;
  557.     MiscCoord_V r, rmin, rmax, r0, rN;
  558.     [self cMin:&cmin cMax:&cmax rMin:&rmin rMax:&rmax forRect:nsrect];
  559.  
  560.     if (cmin >= 0 && cmax >= 0 && rmin >= 0 && rmax >= 0)
  561.     {
  562.     MiscColorList cl;
  563.     id const scroll = [self scroll];
  564.  
  565.     int const NORM_COLOR = cl.store([scroll backgroundColor]) + 1;
  566.     int const HIGH_COLOR = cl.store([scroll selectedBackgroundColor]) + 1;
  567.  
  568.     int const nc = (cmax - cmin) + 1;
  569.     int const nr = (rmax - rmin) + 1;
  570.     int* map = (int*) calloc( nc * nr, sizeof(*map) );
  571.     int* p = map;
  572.  
  573.     for (r = rmin; r <= rmax; r++)    // Transparent and ownerDraw
  574.         {                // cell backgrounds.
  575.         MiscCoord_P const pr = rowBorder->visualToPhysical(r);
  576.         BOOL const row_lit = rowBorder->isSelected(r);
  577.         for (c = cmin; c <= cmax; c++,p++)
  578.         {
  579.         MiscCoord_P const pc = colBorder->visualToPhysical(c);
  580.         BOOL const lit = row_lit || colBorder->isSelected(c);
  581.         id cell = [scroll cellAtRow:pr column:pc];
  582.         if (cell == 0 || ![cell isOpaque])
  583.             *p = lit ? HIGH_COLOR : NORM_COLOR;
  584.         else if ([cell respondsToSelector:@selector(ownerDraw)] &&
  585.             [cell ownerDraw])
  586.             {
  587.             if (lit && [cell respondsToSelector:
  588.                 @selector(selectedBackgroundColor)])
  589.             *p = cl.store( [cell selectedBackgroundColor] ) + 1;
  590.             else if ([cell respondsToSelector:
  591.                 @selector(backgroundColor)])
  592.             *p = cl.store( [cell backgroundColor] ) + 1;
  593.             else
  594.             *p = lit ? HIGH_COLOR : NORM_COLOR;
  595.             }
  596.         }
  597.         }
  598.  
  599.     MiscRectColorList rcl;
  600.     NSRect rect;
  601.     int color;
  602.     while ((color = extract_rect( map, nc, nr, c0, r0, cN, rN )) != 0)
  603.         {
  604.         c0 += cmin; cN += cmin;
  605.         r0 += rmin; rN += rmin;
  606.         MiscPixels const x0 = colBorder->getOffset( c0 );
  607.         MiscPixels const y0 = rowBorder->getOffset( r0 );
  608.         MiscPixels const xN = colBorder->getOffset( cN ) +
  609.                 colBorder->effectiveSize( cN );
  610.         MiscPixels const yN = rowBorder->getOffset( rN ) +
  611.                 rowBorder->effectiveSize( rN );
  612.         rect.origin.x = x0;
  613.         rect.origin.y = y0;
  614.         rect.size.width = (xN - x0);
  615.         rect.size.height = (yN - y0);
  616.  
  617.         rcl.append( rect, cl[ color - 1 ] );
  618.         }
  619.  
  620.     free( map );
  621.  
  622.     MiscPixels x,y;
  623.     MiscPixels const ix0 = colBorder->getOffset( cmin );
  624.     MiscPixels const iy0 = rowBorder->getOffset( rmin );
  625.     MiscPixels const ixN = colBorder->getOffset( cmax ) +
  626.                 colBorder->effectiveSize( cmax );
  627.     MiscPixels const iyN = rowBorder->getOffset( rmax ) +
  628.                 rowBorder->effectiveSize( rmax );
  629.  
  630.     MiscRectList grid_rl;        // Grid drawing list.
  631.     y = iy0;            // Horizontal grid lines.
  632.     rect.origin.x    = floor( (float) ix0 );
  633.     rect.size.width  = floor( (float) (ixN - ix0) );
  634.     rect.size.height = 1;
  635.     for (r = rmin; r <= rmax; r++)
  636.         {
  637.         MiscPixels const h = rowBorder->effectiveSize(r);
  638.         y += h - 1;
  639.         rect.origin.y = floor( (float) y );
  640.         grid_rl.append( rect );
  641.         y++;
  642.         }
  643.  
  644.     x = ix0;            // Vertical grid lines.
  645.     rect.origin.y    = floor( (float) iy0 );
  646.     rect.size.width  = 1;
  647.     rect.size.height = floor( (float) (iyN - iy0) );
  648.     for (c = cmin; c <= cmax; c++)
  649.         {
  650.         MiscPixels const w = colBorder->effectiveSize(c);
  651.         x += w - 1;
  652.         rect.origin.x = floor( (float) x );
  653.         grid_rl.append( rect );
  654.         x++;
  655.         }
  656.  
  657.     rcl.draw();
  658.     grid_rl.draw( [NSColor gridColor] );
  659.  
  660.     NSColor* const fgColor = [scroll textColor];
  661.     NSColor* const hfgColor = [scroll selectedTextColor];
  662.     NSFont* const fnt = [scroll font];
  663.     MiscDrawList dl( [scroll drawClippedText] );
  664.  
  665.     y = iy0;            // Cell contents.
  666.     for (r = rmin; r <= rmax; r++)
  667.         {
  668.         MiscCoord_P const pr = rowBorder->visualToPhysical(r);
  669.         BOOL const row_lit = rowBorder->isSelected(r);
  670.         MiscPixels const h = rowBorder->effectiveSize(r);
  671.         rect.origin.y    = floor( (float) y );
  672.         rect.size.height = floor( (float) (h - 1) );
  673.         x = ix0;
  674.         for (c = cmin; c <= cmax; c++)
  675.         {
  676.         MiscCoord_P const pc = colBorder->visualToPhysical(c);
  677.         BOOL const lit = row_lit || colBorder->isSelected(c);
  678.         MiscPixels const w = colBorder->effectiveSize(c);
  679.         rect.origin.x   = x;
  680.         rect.size.width = (w - 1);
  681.         id cell = [scroll cellAtRow:pr column:pc];
  682.         if (cell == 0 ||
  683.             [cell respondsToSelector:@selector(ownerDraw)] &&
  684.             [cell ownerDraw])
  685.             {
  686.             dl.append( rect, cell, lit,
  687.                 (lit ? hfgColor : fgColor), fnt );
  688.             }
  689.         else
  690.             [cell drawWithFrame:rect inView:self];
  691.         x += w;
  692.         }
  693.         y += h;
  694.         }
  695.  
  696.     dl.draw();
  697.     if ([self shouldDrawCursor])
  698.         [self drawCursorClipTo:NSMakeRect(ix0, iy0, ixN - ix0, iyN - iy0)];
  699.     }
  700.  
  701.     if (need_clip)
  702.     PSgrestore();
  703.     }
  704.  
  705.  
  706. //-----------------------------------------------------------------------------
  707. // - drawCellAtRow:column: -- Physical coords
  708. //-----------------------------------------------------------------------------
  709. - (void)drawCellAtRow:(int)row column:(int)col
  710.     {
  711.     [self drawRect:[self cellFrameAtRow:row column:col]];
  712.     }
  713.  
  714.  
  715. //-----------------------------------------------------------------------------
  716. // - drawRow: -- Physical coord
  717. //-----------------------------------------------------------------------------
  718. - (void)drawRow:(int)row
  719.     {
  720.     if ([self canDraw] && row >= 0 && row < rowBorder->count())
  721.     {
  722.     MiscCoord_V const vRow   = rowBorder->physicalToVisual(row);
  723.     MiscPixels  const offset = rowBorder->getOffset(vRow);
  724.     MiscPixels  const size   = rowBorder->effectiveSize(vRow);
  725.     
  726.     NSRect r = [self visibleRect];
  727.     if (offset + size >= NSMinY(r) && offset < NSMaxY(r))
  728.         {
  729.         r.origin.y = rowBorder->getOffset(vRow);
  730.         r.size.height = rowBorder->effectiveSize(vRow);
  731.         [self setNeedsDisplayInRect:r];
  732.         }
  733.     } 
  734.     }
  735.  
  736.  
  737. //-----------------------------------------------------------------------------
  738. // - drawColumn: -- Physical coord
  739. //-----------------------------------------------------------------------------
  740. - (void)drawColumn:(int)col
  741.     {
  742.     if ([self canDraw] && col >= 0 && col < colBorder->count())
  743.     {
  744.     MiscCoord_V const vCol   = colBorder->physicalToVisual(col);
  745.     MiscPixels  const offset = colBorder->getOffset(vCol);
  746.     MiscPixels  const size   = colBorder->effectiveSize(vCol);
  747.     
  748.     NSRect r = [self visibleRect];
  749.     if (offset + size >= NSMinX(r) && offset < NSMaxX(r))
  750.         {
  751.         r.origin.x = colBorder->getOffset(vCol);
  752.         r.size.width = colBorder->effectiveSize(vCol);
  753.         [self setNeedsDisplayInRect:r];
  754.         }
  755.     } 
  756.     }
  757.  
  758.  
  759. //=============================================================================
  760. // SELECTION
  761. //=============================================================================
  762. //-----------------------------------------------------------------------------
  763. // - setSelectionMode:
  764. //-----------------------------------------------------------------------------
  765. - (void)setSelectionMode:(MiscSelectionMode)mode
  766.     {
  767.     NSZone* const z = [self zone];
  768.     if (tracker != 0)
  769.     [tracker release];
  770.     switch (mode)
  771.     {
  772.     case MISC_LIST_MODE:
  773.         tracker = [MiscListTracker allocWithZone:z];
  774.         break;
  775.     case MISC_RADIO_MODE:
  776.         tracker = [MiscRadioTracker allocWithZone:z];
  777.         break;
  778.     case MISC_HIGHLIGHT_MODE:
  779.         tracker = [MiscHighlightTracker allocWithZone:z];
  780.         break;
  781.     }
  782.     [tracker initBorder:[self borderFor:trackerBorder]];
  783.     }
  784.  
  785.  
  786. //-----------------------------------------------------------------------------
  787. // - selectionChanged
  788. //-----------------------------------------------------------------------------
  789. - (void)selectionChanged
  790.     {
  791.     MiscSparseSet const& newColSel = colBorder->selectionSet();
  792.     MiscSparseSet const& newRowSel = rowBorder->selectionSet();
  793.  
  794.     MiscCoord_V cmin, cmax;
  795.     MiscCoord_V rmin, rmax;
  796.     NSRect vis = [self visibleRect];
  797.     [self cMin:&cmin cMax:&cmax rMin:&rmin rMax:&rmax forRect:vis];
  798.  
  799.     for (MiscCoord_V r = rmin; r <= rmax; r++)
  800.     {
  801.     BOOL rowWasOn = oldRowSel->contains(r);
  802.     BOOL rowIsOn = newRowSel.contains(r);
  803.     for (MiscCoord_V c = cmin; c <= cmax; c++)
  804.         {
  805.         BOOL wasOn = rowWasOn || oldColSel->contains(c);
  806.         BOOL isOn = rowIsOn || newColSel.contains(c);
  807.         if (isOn != wasOn)
  808.         {
  809.         [self setNeedsDisplayInRect:
  810.             NSMakeRect(    colBorder->getOffset(c),
  811.                     rowBorder->getOffset(r),
  812.                     colBorder->effectiveSize(c),
  813.                     rowBorder->effectiveSize(r) )];
  814.         }
  815.         }
  816.     }
  817.     *oldColSel = newColSel;
  818.     *oldRowSel = newRowSel;
  819.     }
  820.  
  821.  
  822. //-----------------------------------------------------------------------------
  823. // - resetSelection
  824. //-----------------------------------------------------------------------------
  825. - (void)resetSelection
  826.     {
  827.     *oldColSel = colBorder->selectionSet();
  828.     *oldRowSel = rowBorder->selectionSet();
  829.     }
  830.  
  831.  
  832. //-----------------------------------------------------------------------------
  833. // - trackBy:
  834. //-----------------------------------------------------------------------------
  835. - (void)trackBy:(MiscBorderType)b
  836.     {
  837.     trackerBorder = b;
  838.     }
  839.  
  840.  
  841. //-----------------------------------------------------------------------------
  842. // - trackingBy
  843. //-----------------------------------------------------------------------------
  844. - (MiscBorderType)trackingBy
  845.     {
  846.     return trackerBorder;
  847.     }
  848.  
  849.  
  850. //=============================================================================
  851. // MOUSE TRACKING
  852. //=============================================================================
  853. //-----------------------------------------------------------------------------
  854. // - constrainSlot:inBorder:
  855. //-----------------------------------------------------------------------------
  856. - (MiscCoord_V)constrainSlot:(MiscCoord_V)s inBorder:(MiscTableBorder*)b
  857.     {
  858.     if (s < 0)
  859.     s = 0;
  860.     else if (s >= b->count())
  861.     s = b->count() - 1;
  862.     return s;
  863.     }
  864.  
  865.  
  866. //-----------------------------------------------------------------------------
  867. // - displayNewSelection
  868. //-----------------------------------------------------------------------------
  869. - (void)displayNewSelection
  870.     {
  871.     id const scroll = [self scroll];
  872.     [scroll selectionChanged];
  873.     [scroll displayIfNeeded];
  874.     }
  875.  
  876.  
  877. //-----------------------------------------------------------------------------
  878. // - sendAction:to:
  879. //
  880. //    NOTE *1*: Conforms to Control's -sendAction:to: which does *not* send
  881. //        any message if the "action" is null.
  882. //-----------------------------------------------------------------------------
  883. - (BOOL)sendAction:(SEL)cmd to:(id)obj
  884.     {
  885.     if (cmd != 0)        // NOTE *1*
  886.     return [NSApp sendAction:cmd to:obj from:[self scroll]];
  887.     return NO;
  888.     }
  889.  
  890.  
  891. //-----------------------------------------------------------------------------
  892. // - setClickedCellFromEvent:
  893. //-----------------------------------------------------------------------------
  894. - (void)setClickedCellFromEvent:(NSEvent*)e
  895.     {
  896.     NSPoint const p = [self convertPoint:[e locationInWindow] fromView:0];
  897.     NSRect b = [self bounds];
  898.     MiscCoord_V const v_row = (p.y < NSMaxY(b) ?
  899.         rowBorder->visualForOffset( (MiscPixels)p.y ) : -1);
  900.     MiscCoord_V const v_col = (p.x < NSMaxX(b) ?
  901.         colBorder->visualForOffset( (MiscPixels)p.x ) : -1);
  902.     MiscCoord_P const p_row = rowBorder->visualToPhysical( v_row );
  903.     MiscCoord_P const p_col = colBorder->visualToPhysical( v_col );
  904.     [[self scroll] setClickedRow:p_row column:p_col];
  905.     }
  906.  
  907.  
  908. //-----------------------------------------------------------------------------
  909. // - cellTrackMouse:atRow:column:
  910. //-----------------------------------------------------------------------------
  911. - (BOOL)cellTrackMouse:(NSEvent*)p
  912.     atRow:(MiscCoord_V)v_row column:(MiscCoord_V)v_col
  913.     {
  914.     BOOL upInCell = NO;
  915.  
  916.     MiscCoord_P const p_col = colBorder->visualToPhysical( v_col );
  917.     MiscCoord_P const p_row = rowBorder->visualToPhysical( v_row );
  918.  
  919.     NSRect r = [self cellInsideAtRow:p_row column:p_col];
  920.     id const scroll = [self scroll];
  921.  
  922.     id cell = [scroll cellAtRow:p_row column:p_col];
  923.     [scroll setClickedRow:p_row column:p_col];
  924.  
  925.     if ([scroll editIfAble:p atRow:p_row column:p_col])
  926.     upInCell = YES;
  927.     else if ([self canPerformDragAtRow:p_row column:p_col withEvent:p])
  928.     upInCell = [self awaitDragEvent:p atRow:p_row column:p_col inRect:r];
  929.     else if ([cell isEnabled])
  930.     {
  931.     NSWindow* win = [self window];
  932.     [self lockFocus];
  933.     [scroll setTracking:YES];
  934.     [cell setCellAttribute:NSCellHighlighted to:1];
  935.     [scroll drawCellAtRow:p_row column:p_col];
  936.     [win flushWindow];
  937.     upInCell = [cell trackMouse:p inRect:r ofView:self
  938.         untilMouseUp:[[cell class] prefersTrackingUntilMouseUp]];
  939.     [scroll setTracking:NO];
  940.     [cell setCellAttribute:NSCellHighlighted to:0];
  941.     [scroll drawCellAtRow:p_row column:p_col];
  942.     [self unlockFocus];
  943.     [win flushWindow];
  944.     }
  945.  
  946.     return upInCell;
  947.     }
  948.  
  949.  
  950. //-----------------------------------------------------------------------------
  951. // - mouseDown:
  952. //-----------------------------------------------------------------------------
  953. - (void)mouseDown:(NSEvent*)p 
  954.     {
  955.     unsigned int const WANTED =
  956.         (NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSPeriodicMask);
  957.  
  958.     id const scroll = [self scroll];
  959.     BOOL const doubleClicked = ([p clickCount] > 1);
  960.     NSPoint evpt = [self convertPoint:[p locationInWindow] fromView:0];
  961.  
  962.     MiscCoord_V const v_col = colBorder->visualForOffset((MiscPixels)evpt.x);
  963.     MiscCoord_V const v_row = rowBorder->visualForOffset((MiscPixels)evpt.y);
  964.     MiscCoord_V slot = (trackerBorder == MISC_COL_BORDER ? v_col : v_row);
  965.     MiscTableBorder* const b = [self borderFor:trackerBorder];
  966.  
  967.     [self eraseCursor];
  968.     [self disableCursor];
  969.  
  970.     [tracker mouseDown:p atPos:slot];
  971.     [self otherBorder:trackerBorder]->selectNone();
  972.     [self displayNewSelection];
  973.  
  974.     BOOL mouseUpInCell = NO;
  975.     if ([scroll isEnabled])
  976.     mouseUpInCell = [self cellTrackMouse:p atRow:v_row column:v_col];
  977.  
  978.     if (!mouseUpInCell)
  979.     {
  980.     [NSEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.1];
  981.     NSEvent* lastEvent = [p copy];
  982.     
  983.     for (;;)
  984.         {
  985.         p = [[self window] nextEventMatchingMask:WANTED];
  986.         if (p == 0 || [p type] == NSLeftMouseUp)
  987.         break;
  988.         else if ([p type] == NSPeriodic)
  989.         [self autoscroll:lastEvent];
  990.         else
  991.         {
  992.         [lastEvent release];
  993.         lastEvent = [p copy];
  994.         }
  995.     
  996.         NSPoint new_loc =
  997.         [self convertPoint:[lastEvent locationInWindow] fromView:0];
  998.         MiscPixels const offset =
  999.         MiscPoint_O( trackerBorder, new_loc ).getX_O();
  1000.         MiscCoord_V const new_slot =
  1001.         offset < MiscRect_O(trackerBorder,[self bounds]).getMaxX_O() ?
  1002.         b->visualForOffset(offset) : b->count();
  1003.         if (new_slot != slot)
  1004.         {
  1005.         slot = new_slot;
  1006.         [tracker mouseDragged:p atPos:slot];
  1007.         [self displayNewSelection];
  1008.         }
  1009.         }
  1010.     
  1011.     [lastEvent release];
  1012.     [NSEvent stopPeriodicEvents];
  1013.     [self setClickedCellFromEvent:p];
  1014.     }
  1015.  
  1016.     [tracker mouseUp:p atPos:slot];
  1017.     [self displayNewSelection];
  1018.     [scroll border:trackerBorder setCursorSlot:
  1019.         b->visualToPhysical([self constrainSlot:slot inBorder:b])];
  1020.  
  1021.     [self enableCursor];
  1022.     if ([self shouldDrawCursor])
  1023.     [self drawCursor];
  1024.     [[self window] flushWindow];
  1025.  
  1026.     if ([scroll isEnabled])
  1027.     {
  1028.     [scroll sendAction];
  1029.     if (doubleClicked && ![scroll isEditing])    // FIXME: not right
  1030.         [scroll sendDoubleAction];
  1031.     }
  1032.     }
  1033.  
  1034.  
  1035. //-----------------------------------------------------------------------------
  1036. // *FIXME*
  1037. //    OPENSTEP 4.2 Objective-C++ compiler for NT (final release) crashes 
  1038. //    whenever a message is sent to 'super' from within a category.  This 
  1039. //    bug also afflicts the 4.2 (prerelease) compiler for Mach and NT.  
  1040. //    Work around it by providing stub methods in the main (non-category) 
  1041. //    implementation which merely forward the appropriate message to 'super' 
  1042. //    on behalf of the categories.  Though ugly, it works, is very 
  1043. //    localized, and simple to remove when the bug is finally fixed.  
  1044. //-----------------------------------------------------------------------------
  1045. - (void)superPrint:(id)sender { [super print:sender]; }
  1046.  
  1047. @end
  1048.