home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscTableScroll / MiscTableView.M < prev    next >
Encoding:
Text File  |  1996-02-11  |  27.2 KB  |  913 lines

  1. //=============================================================================
  2. //
  3. //        Copyright (C) 1995, 1996 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.8 96/01/13 23:46:17 zarnuk Exp $
  27. // $Log:        MiscTableView.M,v $
  28. //    Revision 1.8  96/01/13    23:46:17  zarnuk
  29. //    All allocations are now done from [self zone].
  30. //    
  31. //    Revision 1.7  95/10/20    00:13:39  sunshine
  32. //    Was including MiscTableScroll.h with "" instead of <>.
  33. //    
  34. //    Revision 1.6  95/10/19    05:42:00  sunshine
  35. //    Now sends -setSelFont: to FontManager at appropriate times so that font
  36. //    selection in FontManager is up-to-date.
  37. //    
  38. //    Revision 1.5  95/10/08    16:12:30  zarnuk
  39. //    Cell* --> id.
  40. //-----------------------------------------------------------------------------
  41. #import "MiscTableView.h"
  42. #import "MiscHighlightTracker.h"
  43. #import "MiscListTracker.h"
  44. #import "MiscRadioTracker.h"
  45. #import "MiscSparseSet.h"
  46. #import "MiscTableBorder.h"
  47. #import "MiscTableFocus.h"
  48. #import <misckit/MiscTableScroll.h>
  49.  
  50. #import <new.h>
  51.  
  52. extern "Objective-C" {
  53. #import <appkit/Application.h>
  54. #import <appkit/Cell.h>
  55. #import <appkit/FontManager.h>
  56. #import <appkit/timer.h>
  57. }
  58.  
  59. extern "C" {
  60. #import <math.h>        // for floor()
  61. }
  62.  
  63.  
  64. //----------------------------------------------------------------------------
  65. // startTimer
  66. //----------------------------------------------------------------------------
  67. static inline void startTimer( NXTrackingTimer*& timer )
  68.     {
  69.     if (timer == 0)
  70.         timer = NXBeginTimer( 0, 0.1, 0.1 );
  71.     }
  72.  
  73.  
  74. //----------------------------------------------------------------------------
  75. // stopTimer
  76. //----------------------------------------------------------------------------
  77. static inline void stopTimer( NXTrackingTimer*& timer )
  78.     {
  79.     if (timer)
  80.         {
  81.         NXEndTimer( timer );
  82.         timer = 0;
  83.         }
  84.     }
  85.  
  86.  
  87.  
  88. //=============================================================================
  89. // IMPLEMENTATION
  90. //=============================================================================
  91. @implementation MiscTableView
  92.  
  93. //-----------------------------------------------------------------------------
  94. // - initFrame:
  95. //
  96. //        NOTE *1*: Default behavior is to take keyboard-cursor information from
  97. //                the row-border.
  98. //-----------------------------------------------------------------------------
  99. - initFrame:(NXRect const*)frameRect
  100.         scroll:(MiscTableScroll*)i_scroll
  101.         colInfo:(MiscTableBorder*)i_col_border
  102.         rowInfo:(MiscTableBorder*)i_row_border
  103.     {
  104.     NXZone* const z = [self zone];
  105.     NXRect rect;
  106.     if (frameRect != 0)
  107.         rect.origin = frameRect->origin;
  108.     else
  109.         {
  110.         rect.origin.x = 0;
  111.         rect.origin.y = 0;
  112.         }
  113.  
  114.     rect.size.width = i_col_border->totalSize();
  115.     rect.size.height = i_row_border->totalSize();
  116.  
  117.     [super initFrame:&rect];
  118.     [self setFlipped:YES];
  119.     [self setClipping:NO];
  120.     [self setOpaque:YES];
  121.  
  122.     scroll = i_scroll;
  123.     col_border = i_col_border;
  124.     row_border = i_row_border;
  125.     tracker_border = MISC_ROW_BORDER;                    // NOTE *1*
  126.  
  127.     oldColSel = new( NXZoneMalloc(z,sizeof(*oldColSel)) ) MiscSparseSet;
  128.     oldRowSel = new( NXZoneMalloc(z,sizeof(*oldRowSel)) ) MiscSparseSet;
  129.     [self setSelectionMode: [scroll selectionMode]];
  130.  
  131.     focuser = [[MiscTableFocus allocFromZone:z] initOwner: self];
  132.     return self;
  133.     }
  134.  
  135.  
  136. //-----------------------------------------------------------------------------
  137. // - free
  138. //-----------------------------------------------------------------------------
  139. - free
  140.     {
  141.     [focuser free];
  142.     [tracker free];
  143.     NXZone* const z = [self zone];
  144.     if (oldColSel != 0)
  145.         {
  146.         oldColSel->MiscSparseSet::~MiscSparseSet();
  147.         NXZoneFree( z, oldColSel );
  148.         }
  149.     if (oldRowSel != 0)
  150.         {
  151.         oldRowSel->MiscSparseSet::~MiscSparseSet();
  152.         NXZoneFree( z, oldRowSel );
  153.         }
  154.     return [super free];
  155.     }
  156.  
  157.  
  158. //-----------------------------------------------------------------------------
  159. // TYPE VARIATIONS
  160. //-----------------------------------------------------------------------------
  161.  
  162. - (MiscTableBorder*) borderFor: (MiscBorderType)b
  163.         { return (b == MISC_ROW_BORDER ? row_border : col_border); }
  164. - (MiscBorderType) otherBorder: (MiscBorderType)b
  165.         { return (b == MISC_ROW_BORDER ? MISC_COL_BORDER : MISC_ROW_BORDER); }
  166. - (BOOL) isHorz: (MiscBorderType)b { return (b == MISC_COL_BORDER); }
  167.  
  168. - (NXCoord) border:(MiscBorderType)b pointX: (NXPoint const*) point
  169.         { return [self isHorz:b] ? point->x : point->y; }
  170. - (NXCoord) border:(MiscBorderType)b pointY: (NXPoint const*) point
  171.         { return [self isHorz:b] ? point->y : point->x; }
  172.  
  173. - (NXCoord) border:(MiscBorderType)b rectMinX:(NXRect const*)frameRect
  174.         { return [self isHorz:b] ? NX_X(frameRect) : NX_Y(frameRect); }
  175. - (NXCoord) border:(MiscBorderType)b rectMaxX:(NXRect const*)frameRect
  176.         { return [self isHorz:b] ? NX_MAXX(frameRect) : NX_MAXY(frameRect); }
  177. - (NXCoord) border:(MiscBorderType)b rectMinY:(NXRect const*)frameRect
  178.         { return [self isHorz:b] ? NX_Y(frameRect) : NX_X(frameRect); }
  179. - (NXCoord) border:(MiscBorderType)b rectMaxY:(NXRect const*)frameRect
  180.         { return [self isHorz:b] ? NX_MAXY(frameRect) : NX_MAXX(frameRect); }
  181. - (NXCoord) border:(MiscBorderType)b rectWidth:(NXRect const*)frameRect
  182.         { return [self isHorz:b] ? NX_WIDTH(frameRect) : NX_HEIGHT(frameRect); }
  183. - (NXCoord) border:(MiscBorderType)b rectHeight:(NXRect const*)frameRect
  184.         { return [self isHorz:b] ? NX_HEIGHT(frameRect) : NX_WIDTH(frameRect); }
  185.  
  186. - (void) border:(MiscBorderType)b setRect:(NXRect*)rect minX:(NXCoord)x
  187.         { if ([self isHorz:b]) NX_X(rect) = x; else NX_Y(rect) = x; }
  188. - (void) border:(MiscBorderType)b setRect:(NXRect*)rect minY:(NXCoord)y
  189.         { if ([self isHorz:b]) NX_Y(rect) = y; else NX_X(rect) = y; }
  190. - (void) border:(MiscBorderType)b setRect:(NXRect*)rect width:(NXCoord)width
  191.         { if ([self isHorz:b]) NX_WIDTH(rect) = width;
  192.                 else NX_HEIGHT(rect) = width; }
  193. - (void) border:(MiscBorderType)b setRect:(NXRect*)rect height:(NXCoord)height
  194.         { if ([self isHorz:b]) NX_HEIGHT(rect) = height;
  195.                 else NX_WIDTH(rect) = height; }
  196.  
  197.  
  198. //-----------------------------------------------------------------------------
  199. // drewCellAt:border:oldSel:
  200. //
  201. //        Keeps the old selection sets up to date whenever we draw a cell.  This
  202. //        way -reflectSelection has valid data to work from.
  203. //-----------------------------------------------------------------------------
  204. - (void) drewCellAt: (MiscCoord_V)slot border: (MiscTableBorder*)b
  205.     oldSel: (MiscSparseSet*)oldSel
  206.     {
  207.     BOOL isSelected = b->selectionSet().contains( slot );
  208.     if (isSelected != oldSel->contains(slot))
  209.         {
  210.         if (isSelected)
  211.             oldSel->add( slot );
  212.         else
  213.             oldSel->remove( slot );
  214.         }
  215.     }
  216.  
  217.  
  218. //-----------------------------------------------------------------------------
  219. // drewCellAt::
  220. //-----------------------------------------------------------------------------
  221. - (void) drewCellAt: (MiscCoord_V)r : (MiscCoord_V)c
  222.     {
  223.     [self drewCellAt:r border:row_border oldSel: oldRowSel];
  224.     [self drewCellAt:c border:col_border oldSel: oldColSel];
  225.     }
  226.  
  227.  
  228. //-----------------------------------------------------------------------------
  229. // - getCellFrame:at:: -- Visual coords
  230. //-----------------------------------------------------------------------------
  231. - getCellFrame: (NXRect*)r at: (int)row : (int)col
  232.     {
  233.     if (row < 0 || row >= row_border->count() ||
  234.         col < 0 || col >= col_border->count())
  235.         NXSetRect( r, 0, 0, 0, 0 );
  236.     else
  237.         NXSetRect( r, col_border->getOffset(col), row_border->getOffset(row),
  238.                 col_border->effectiveSize(col),row_border->effectiveSize(row) );
  239.     return self;
  240.     }
  241.  
  242.  
  243. //-----------------------------------------------------------------------------
  244. // - getVisFrame:at:from:
  245. //-----------------------------------------------------------------------------
  246. - (void) getVisFrame:(NXRect*)r at:(MiscCoord_V)slot from:(MiscBorderType)bdr
  247.     {
  248.     MiscTableBorder* const b = [self borderFor:bdr];
  249.     [self getVisibleRect: r];
  250.     [self border:bdr setRect:r minX:b->getOffset(slot)];
  251.     [self border:bdr setRect:r width:b->effectiveSize(slot)];
  252.     }
  253.  
  254.  
  255. //-----------------------------------------------------------------------------
  256. // - firstVisibleSlot:
  257. //-----------------------------------------------------------------------------
  258. - (MiscCoord_V) firstVisibleSlot: (MiscBorderType)bdr
  259.     {
  260.     MiscCoord_V ret = -1;
  261.     MiscTableBorder* const b = [self borderFor:bdr];
  262.     if (b->count() > 0)
  263.         {
  264.         NXRect r; [self getVisibleRect: &r];
  265.         MiscPixels vis_origin = MiscPixels( [self border:bdr rectMinX:&r] );
  266.         ret = b->visualForOffset( vis_origin );
  267.         MiscPixels slot_origin = b->getOffset( ret );
  268.         if (slot_origin < vis_origin)
  269.             ret++;
  270.         }
  271.     return ret;
  272.     }
  273.  
  274.  
  275. //-----------------------------------------------------------------------------
  276. // - acceptsFirstMouse
  277. //-----------------------------------------------------------------------------
  278. - (BOOL) acceptsFirstMouse
  279.     {
  280.     return YES;
  281.     }
  282.  
  283.  
  284. //-----------------------------------------------------------------------------
  285. // - acceptsFirstResponder
  286. //-----------------------------------------------------------------------------
  287. - (BOOL) acceptsFirstResponder
  288.     {
  289.     return YES;
  290.     }
  291.  
  292.  
  293. //-----------------------------------------------------------------------------
  294. // - setFocusFrame
  295. //-----------------------------------------------------------------------------
  296. - (void) setFocusFrame
  297.     {
  298.     NXRect r = {{ 0,0 }, { 0,0 }};
  299.     if (row_border->count() > 0 && col_border->count() > 0)
  300.         {
  301.         MiscTableBorder* const b = [self borderFor: tracker_border];
  302.         MiscCoord_V focusSlot = b->getCursor();
  303.         if (focusSlot < 0 || focusSlot >= b->count())
  304.             focusSlot = (b->hasSelection() ? b->selectedSlot() :
  305.                         [self firstVisibleSlot: tracker_border]);
  306.         b->setCursor( focusSlot );
  307.         [self getVisFrame:&r at:focusSlot from:tracker_border];
  308.         }
  309.     [focuser setFrame: &r];
  310.     }
  311.  
  312.  
  313. //-----------------------------------------------------------------------------
  314. // - startFocus
  315. //-----------------------------------------------------------------------------
  316. - (void) startFocus
  317.     {
  318.     [self setFocusFrame];
  319.     [focuser startFocus];
  320.     }
  321.  
  322.  
  323. //-----------------------------------------------------------------------------
  324. // - stopFocus
  325. //-----------------------------------------------------------------------------
  326. - (void) stopFocus
  327.     {
  328.     [focuser stopFocus];
  329.     }
  330.  
  331.  
  332. //-----------------------------------------------------------------------------
  333. // - becomeFirstResponder
  334. //-----------------------------------------------------------------------------
  335. - becomeFirstResponder
  336.     {
  337.     Window* win = [self window];
  338.     if (win && [win isKeyWindow])
  339.         {
  340.         [self startFocus];
  341.         [[FontManager new] setSelFont:[scroll font] isMultiple:NO];
  342.         }
  343.     return self;
  344.     }
  345.  
  346.  
  347. //-----------------------------------------------------------------------------
  348. // - resignFirstResponder
  349. //-----------------------------------------------------------------------------
  350. - resignFirstResponder
  351.     {
  352.     [self stopFocus];
  353.     return self;
  354.     }
  355.  
  356.  
  357. //-----------------------------------------------------------------------------
  358. // - becomeKeyWindow
  359. //-----------------------------------------------------------------------------
  360. - becomeKeyWindow
  361.     {
  362.     [self startFocus];
  363.     [[FontManager new] setSelFont:[scroll font] isMultiple:NO];
  364.     return self;
  365.     }
  366.  
  367.  
  368. //-----------------------------------------------------------------------------
  369. // - resignKeyWindow
  370. //-----------------------------------------------------------------------------
  371. - resignKeyWindow
  372.     {
  373.     [self stopFocus];
  374.     return self;
  375.     }
  376.  
  377.  
  378. //-----------------------------------------------------------------------------
  379. // - windowChanged:
  380. //-----------------------------------------------------------------------------
  381. - windowChanged: newWindow
  382.     {
  383.     [self stopFocus];
  384.     return self;
  385.     }
  386.  
  387.  
  388. //-----------------------------------------------------------------------------
  389. // - resetCursorRects
  390. //
  391. //        NOTE: This catches srolling.  The blinking cursor is always placed
  392. //                within the visibleRect.     After scrolling the visibleRect has
  393. //                changed so we need to readjust the cursor frame.
  394. //-----------------------------------------------------------------------------
  395. - resetCursorRects
  396.     {
  397.     [self setFocusFrame];
  398.     return self;
  399.     }
  400.  
  401.  
  402. //-----------------------------------------------------------------------------
  403. // - sizeTo::
  404. //-----------------------------------------------------------------------------
  405. - sizeTo: (NXCoord)width : (NXCoord)height
  406.     {
  407.     [super sizeTo: width : height];
  408.     [self setFocusFrame];
  409.     return self;
  410.     }
  411.  
  412.  
  413. //-----------------------------------------------------------------------------
  414. // - setFrame:
  415. //-----------------------------------------------------------------------------
  416. - setFrame: (NXRect const*)r
  417.     {
  418.     [super setFrame: r];
  419.     [self setFocusFrame];
  420.     return self;
  421.     }
  422.  
  423.  
  424. //-----------------------------------------------------------------------------
  425. // - adjustSize
  426. //-----------------------------------------------------------------------------
  427. - (void) adjustSize
  428.     {
  429.     [self sizeTo: col_border->totalSize() : row_border->totalSize()];
  430.     }
  431.  
  432.  
  433. //-----------------------------------------------------------------------------
  434. // - scrollCellToVisible:: -- Physical coords
  435. //-----------------------------------------------------------------------------
  436. - (void) scrollCellToVisible: (int)row : (int) col
  437.     {
  438.     NXRect r;
  439.     [self getCellFrame: &r at:
  440.                         row_border->physicalToVisual(row) :
  441.                         col_border->physicalToVisual(col)];
  442.     [self scrollRectToVisible: &r];
  443.     }
  444.  
  445.  
  446. //-----------------------------------------------------------------------------
  447. // - border:scrollToVisible: -- Physical coord
  448. //-----------------------------------------------------------------------------
  449. - (void) border: (MiscBorderType)bdr scrollToVisible: (MiscCoord_P)slot
  450.     {
  451.     NXRect r;
  452.     MiscTableBorder* const b = [self borderFor:bdr];
  453.     [self getVisFrame:&r at:b->physicalToVisual(slot) from:bdr];
  454.     [self scrollRectToVisible:&r];
  455.     }
  456.  
  457.  
  458. //-----------------------------------------------------------------------------
  459. // - scrollRowToVisible: -- Physical coord
  460. //-----------------------------------------------------------------------------
  461. - (void) scrollRowToVisible: (int)row
  462.     {
  463.     [self border:MISC_ROW_BORDER scrollToVisible:row];
  464.     }
  465.  
  466.  
  467. //-----------------------------------------------------------------------------
  468. // - scrollColToVisible: -- Physical coord
  469. //-----------------------------------------------------------------------------
  470. - (void) scrollColToVisible: (int)col
  471.     {
  472.     [self border:MISC_COL_BORDER scrollToVisible:col];
  473.     }
  474.  
  475.  
  476. //-----------------------------------------------------------------------------
  477. // - cMin:cMax:rMin:rMax:forRect:
  478. //-----------------------------------------------------------------------------
  479. - (void) cMin: (MiscCoord_V*)cmin cMax: (MiscCoord_V*)cmax
  480.          rMin: (MiscCoord_V*)rmin rMax: (MiscCoord_V*)rmax
  481.          forRect: (NXRect const*) nxrect
  482.     {
  483.     *cmin = col_border->visualForOffset( (MiscPixels) nxrect->origin.x );
  484.     *cmax = col_border->visualForOffset( 
  485.                 (MiscPixels) (nxrect->origin.x + nxrect->size.width) - 1 );
  486.     *rmin = row_border->visualForOffset( (MiscPixels) nxrect->origin.y );
  487.     *rmax = row_border->visualForOffset(
  488.                 (MiscPixels) (nxrect->origin.y + nxrect->size.height) - 1 );
  489.     }
  490.  
  491.  
  492. //-----------------------------------------------------------------------------
  493. // - drawRect:
  494. //-----------------------------------------------------------------------------
  495. - (void) drawRect: (NXRect const*) nxrect
  496.     {
  497.     MiscCoord_V c, cmin, cmax;
  498.     MiscCoord_V r, rmin, rmax;
  499.     [self cMin:&cmin cMax:&cmax rMin:&rmin rMax:&rmax forRect:nxrect];
  500.  
  501.     if (cmin >= 0 && cmax >= 0 && rmin >= 0 && rmax >= 0)
  502.         {
  503.         NXCoord const x0 = floor( (float) col_border->getOffset( cmin ) );
  504.         NXCoord const y0 = floor( (float) row_border->getOffset( rmin ) );
  505.         NXRect rect = { {x0,y0}, {0,0} };
  506.     
  507.         for (r = rmin;    r <= rmax;    r++)
  508.             {
  509.             rect.size.height = floor( (float) row_border->effectiveSize(r) );
  510.             for (c = cmin;    c <= cmax;    c++)
  511.                 {
  512.                 rect.size.width = floor( (float)col_border->effectiveSize(c) );
  513.                 id cell = [scroll cellAt: row_border->visualToPhysical(r) :
  514.                                              col_border->visualToPhysical(c)];
  515.                 [cell drawSelf:&rect inView:self];
  516.                 [self drewCellAt:r:c];
  517.                 rect.origin.x = floor( rect.origin.x + rect.size.width );
  518.                 }
  519.             rect.origin.x = x0;
  520.             rect.origin.y = floor( rect.origin.y + rect.size.height );
  521.             }
  522.         }
  523.     }
  524.  
  525.  
  526. //-----------------------------------------------------------------------------
  527. // - drawSelf::
  528. //-----------------------------------------------------------------------------
  529. - drawSelf: (NXRect const*) rects :(int) nrects
  530.     {
  531.     if (nrects == 1)
  532.         [self drawRect: rects];
  533.     else if (nrects == 3)
  534.         {
  535.         [self drawRect: ++rects];
  536.         [self drawRect: ++rects];
  537.         }
  538.     return self;
  539.     }
  540.  
  541.  
  542. //-----------------------------------------------------------------------------
  543. // - drawCellAt::updateSel: -- Physical coords
  544. //-----------------------------------------------------------------------------
  545. - (void) drawCellAt: (int)row : (int)col updateSel: (BOOL)updateSel
  546.     {
  547.     if ([self canDraw] &&
  548.         row >= 0 && row < row_border->count() &&
  549.         col >= 0 && col < col_border->count())
  550.         {
  551.         NXRect v;        [self getVisibleRect: &v];
  552.         MiscCoord_V const vRow = row_border->physicalToVisual(row);
  553.         MiscCoord_V const vCol = col_border->physicalToVisual(col);
  554.  
  555.         NXRect const r = {
  556.         { col_border->getOffset(vCol), row_border->getOffset(vRow) },
  557.         { col_border->effectiveSize(vCol), row_border->effectiveSize(vRow) }};
  558.  
  559.         if (NXIntersectsRect( &r, &v ))
  560.             {
  561.             id const cell = [scroll cellAt: row : col];
  562.             BOOL const didLock = ![self isFocusView];
  563.             if (didLock) [self lockFocus];
  564.             [cell drawSelf: &r inView: self];
  565.             if (updateSel)
  566.                 [self drewCellAt: vRow : vCol];
  567.             if (didLock) [self unlockFocus];
  568.             }
  569.         }
  570.     }
  571.  
  572.  
  573. //-----------------------------------------------------------------------------
  574. // - drawCellAt:: -- Physical coords
  575. //-----------------------------------------------------------------------------
  576. - drawCellAt: (int)row : (int)col
  577.     {
  578.     [self drawCellAt:row:col updateSel:YES];
  579.     return self;
  580.     }
  581.  
  582.  
  583. //-----------------------------------------------------------------------------
  584. // - drawRow: -- Physical coord
  585. //-----------------------------------------------------------------------------
  586. - drawRow: (int)row
  587.     {
  588.     if ([self canDraw] && row >= 0 && row < row_border->count())
  589.         {
  590.         MiscCoord_V const vRow = row_border->physicalToVisual(row);
  591.     
  592.         NXRect r;
  593.         [self getVisibleRect: &r];
  594.         r.origin.y = row_border->getOffset(vRow);
  595.         r.size.height = row_border->effectiveSize(vRow);
  596.  
  597.         BOOL const didLock = ![self isFocusView];
  598.         if (didLock) [self lockFocus];
  599.         [self drawRect: &r];
  600.         if (didLock) [self unlockFocus];
  601.         }
  602.     return self;
  603.     }
  604.  
  605.  
  606. //-----------------------------------------------------------------------------
  607. // - drawCol: -- Physical coord
  608. //-----------------------------------------------------------------------------
  609. - drawCol: (int)col
  610.     {
  611.     if ([self canDraw] && col >= 0 && col < col_border->count())
  612.         {
  613.         MiscCoord_V const vCol = col_border->physicalToVisual(col);
  614.     
  615.         NXRect r;
  616.         [self getVisibleRect: &r];
  617.         r.origin.x = col_border->getOffset(vCol);
  618.         r.size.width = col_border->effectiveSize(vCol);
  619.  
  620.         BOOL const didLock = ![self isFocusView];
  621.         if (didLock) [self lockFocus];
  622.         [self drawRect: &r];
  623.         if (didLock) [self unlockFocus];
  624.         }
  625.     return self;
  626.     }
  627.  
  628.  
  629. //-----------------------------------------------------------------------------
  630. // - setSelectionMode:
  631. //-----------------------------------------------------------------------------
  632. - (void) setSelectionMode: (MiscSelectionMode) mode
  633.     {
  634.     NXZone* const z = [self zone];
  635.     if (tracker != 0)
  636.         [tracker free];
  637.     switch (mode)
  638.         {
  639.         case MISC_LIST_MODE:
  640.             tracker = [MiscListTracker allocFromZone:z];
  641.             break;
  642.         case MISC_RADIO_MODE:
  643.             tracker = [MiscRadioTracker allocFromZone:z];
  644.             break;
  645.         case MISC_HIGHLIGHT_MODE:
  646.             tracker = [MiscHighlightTracker allocFromZone:z];
  647.             break;
  648.         }
  649.     [tracker initBorder: [self borderFor:tracker_border]];
  650.     }
  651.  
  652.  
  653. //-----------------------------------------------------------------------------
  654. // - reflectSelection
  655. //-----------------------------------------------------------------------------
  656. - (void) reflectSelection
  657.     {
  658.     if ([self canDraw])
  659.         {
  660.         MiscSparseSet const& newColSel = col_border->selectionSet();
  661.         MiscSparseSet const& newRowSel = row_border->selectionSet();
  662.  
  663.         MiscCoord_V cmin, cmax;
  664.         MiscCoord_V rmin, rmax;
  665.         NXRect vis;        [self getVisibleRect: &vis];
  666.         [self cMin:&cmin cMax:&cmax rMin:&rmin rMax:&rmax forRect:&vis];
  667.     
  668.         [window disableFlushWindow];
  669.         BOOL locked = NO;
  670.         for (MiscCoord_V r = rmin; r <= rmax; r++)
  671.             {
  672.             BOOL rowWasOn = oldRowSel->contains(r);
  673.             BOOL rowIsOn = newRowSel.contains(r);
  674.             for (MiscCoord_V c = cmin; c <= cmax; c++)
  675.                 {
  676.                 BOOL wasOn = rowWasOn || oldColSel->contains(c);
  677.                 BOOL isOn = rowIsOn || newColSel.contains(c);
  678.                 if (isOn != wasOn)
  679.                     {
  680.                     if (!locked)
  681.                         {
  682.                         locked = YES;
  683.                         [self lockFocus];
  684.                         }
  685.                     MiscCoord_P const pCol = col_border->visualToPhysical(c);
  686.                     MiscCoord_P const pRow = row_border->visualToPhysical(r);
  687.                     [self drawCellAt: pRow : pCol updateSel: NO];
  688.                     }
  689.                 }
  690.             }
  691.         if (locked)
  692.             [self unlockFocus];
  693.         [[window reenableFlushWindow] flushWindow];
  694.  
  695.         *oldColSel = newColSel;
  696.         *oldRowSel = newRowSel;
  697.         }
  698.     }
  699.  
  700.  
  701. //-----------------------------------------------------------------------------
  702. // - trackBy:
  703. //-----------------------------------------------------------------------------
  704. - (void) trackBy: (MiscBorderType)b
  705.     {
  706.     tracker_border = b;
  707.     [self setFocusFrame];
  708.     }
  709.  
  710.  
  711. //-----------------------------------------------------------------------------
  712. // - trackingBy
  713. //-----------------------------------------------------------------------------
  714. - (MiscBorderType) trackingBy
  715.     {
  716.     return tracker_border;
  717.     }
  718.  
  719.  
  720. //-----------------------------------------------------------------------------
  721. // - reflectCursor
  722. //-----------------------------------------------------------------------------
  723. - (void) reflectCursor
  724.     {
  725.     [self setFocusFrame];
  726.     }
  727.  
  728.  
  729. //-----------------------------------------------------------------------------
  730. // constrainSlot:inBorder:
  731. //-----------------------------------------------------------------------------
  732. - (MiscCoord_V) constrainSlot:(MiscCoord_V)s inBorder: (MiscTableBorder*)b
  733.     {
  734.     if (s < 0)
  735.         s = 0;
  736.     else if (s >= b->count())
  737.         s = b->count() - 1;
  738.     return s;
  739.     }
  740.  
  741.  
  742. //-----------------------------------------------------------------------------
  743. // - mouseDown:
  744. //-----------------------------------------------------------------------------
  745. - mouseDown: (NXEvent*) p
  746.     {
  747.     BOOL doubleClicked = (p->data.mouse.click > 1);
  748.     NXPoint evpt = p->location;
  749.     [self convertPoint: &evpt fromView: 0];
  750.     MiscTableBorder* const b = [self borderFor:tracker_border];
  751.     MiscCoord_V slot = b->visualForOffset(
  752.                 MiscPixels([self border:tracker_border pointX:&evpt]) );
  753.     int const WANTED = (NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK | NX_TIMERMASK);
  754.  
  755.     [focuser stopFocus];
  756.     [tracker mouseDown: p atPos: slot];
  757.     [self borderFor: [self otherBorder:tracker_border]]->selectionSet().empty();
  758.     [scroll reflectSelection];
  759.  
  760.     int old_mask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  761.     NXTrackingTimer* timer = 0;
  762.     startTimer( timer );
  763.     NXEvent lastEvent = *p;
  764.  
  765.     for (;;)
  766.         {
  767.         p = [NXApp getNextEvent: WANTED];
  768.         if (p == 0 || p->type == NX_MOUSEUP)
  769.             break;
  770.         else if (p->type == NX_TIMER)
  771.             [self autoscroll: &lastEvent];
  772.         else
  773.             lastEvent = *p;
  774.  
  775.         NXPoint new_loc = lastEvent.location;
  776.         [self convertPoint:&new_loc fromView:0];
  777.         MiscPixels const offset =
  778.                 MiscPixels( [self border:tracker_border pointX:&new_loc] );
  779.         MiscCoord_V const new_slot =
  780.                 (offset < [self border:tracker_border rectMaxX:&bounds] ?
  781.                 b->visualForOffset(offset) : b->count());
  782.         if (new_slot != slot)
  783.             {
  784.             slot = new_slot;
  785.             [tracker mouseDragged: p atPos: slot];
  786.             [scroll reflectSelection];
  787.             }
  788.         }
  789.  
  790.     stopTimer( timer );
  791.     [window setEventMask:old_mask];
  792.  
  793.     [tracker mouseUp: p atPos: slot];
  794.     [scroll reflectSelection];
  795.     [scroll border: tracker_border setCursor:
  796.                 b->visualToPhysical([self constrainSlot:slot inBorder:b])];
  797.     [scroll selectText: self];
  798.     [focuser startFocus];
  799.  
  800.     if ([scroll isEnabled])
  801.         {
  802.         if (doubleClicked)
  803.             [scroll sendDoubleAction];
  804.         else
  805.             [scroll sendAction];
  806.         }
  807.     return self;
  808.     }
  809.  
  810.  
  811. //-----------------------------------------------------------------------------
  812. // - moveFocusBy:
  813. //-----------------------------------------------------------------------------
  814. - (void) moveFocusBy: (int) delta
  815.     {
  816.     MiscTableBorder* const b = [self borderFor: tracker_border];
  817.     int const lim = b->count();
  818.     if ([focuser isShowingFocus] && lim > 0)
  819.         {
  820.         MiscCoord_V focusSlot = b->getCursor() + delta;
  821.         if (focusSlot < 0)
  822.             focusSlot = lim - 1;
  823.         else if (focusSlot >= lim)
  824.             focusSlot = 0;
  825.         b->setCursor( focusSlot );
  826.         [self border:tracker_border scrollToVisible:
  827.                         b->visualToPhysical(focusSlot)];
  828.         [self setFocusFrame];
  829.         }
  830.     }
  831.  
  832.  
  833. //-----------------------------------------------------------------------------
  834. // - gotoNextText
  835. //-----------------------------------------------------------------------------
  836. - (void) gotoNextText
  837.     {
  838.     id targ = [scroll nextText];
  839.     if (targ && [targ respondsTo: @selector(selectText:)])
  840.         [targ selectText: scroll];
  841.     }
  842.  
  843.  
  844. //-----------------------------------------------------------------------------
  845. // - gotoPreviousText
  846. //-----------------------------------------------------------------------------
  847. - (void) gotoPreviousText
  848.     {
  849.     id targ = [scroll previousText];
  850.     if (targ && [targ respondsTo: @selector(selectText:)])
  851.         [targ selectText: scroll];
  852.     }
  853.  
  854.  
  855. //-----------------------------------------------------------------------------
  856. // - keyboardSelect:
  857. //-----------------------------------------------------------------------------
  858. - (void) keyboardSelect: (NXEvent const*)p
  859.     {
  860.     [tracker keyDown: p atPos: [self borderFor:tracker_border]->getCursor()];
  861.     [scroll borderClearSelection:[self otherBorder: tracker_border]];
  862.     [scroll reflectSelection];
  863.     if ([scroll isEnabled])
  864.         [scroll sendAction];
  865.     }
  866.  
  867.  
  868. //-----------------------------------------------------------------------------
  869. // - keyboardPerform
  870. //-----------------------------------------------------------------------------
  871. - (void) keyboardPerform
  872.     {
  873.     if ([scroll isEnabled])
  874.         [scroll sendDoubleAction];
  875.     }
  876.  
  877.  
  878. //-----------------------------------------------------------------------------
  879. // - keyDown:
  880. //-----------------------------------------------------------------------------
  881. - keyDown: (NXEvent*) p
  882.     {
  883.     enum
  884.         {
  885.         K_TAB    = '\t',
  886.         K_BTAB    = 0x19,
  887.         K_RETURN= '\r',
  888.         K_SPACE = ' ' ,
  889.         K_LEFT    = 0xac,
  890.         K_UP    = 0xad,
  891.         K_RIGHT = 0xae,
  892.         K_DOWN    = 0xaf,
  893.         };
  894.  
  895.     if ((p->flags & NX_COMMANDMASK) == 0)
  896.         {
  897.         switch (p->data.key.charCode)
  898.             {
  899.             case K_UP            : [self moveFocusBy: -1            ]; break;
  900.             case K_DOWN            : [self moveFocusBy:  1            ]; break;
  901.             case K_LEFT            : [self moveFocusBy: -1            ]; break;
  902.             case K_RIGHT        : [self moveFocusBy:  1            ]; break;
  903.             case K_TAB            : [self gotoNextText            ]; break;
  904.             case K_BTAB            : [self gotoPreviousText        ]; break;
  905.             case K_SPACE        : [self keyboardSelect: p        ]; break;
  906.             case K_RETURN        : [self keyboardPerform            ]; break;
  907.             }
  908.         }
  909.     return self;
  910.     }
  911.  
  912. @end
  913.