home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / YellowBox / Kits / MiscTableScroll-138.1 / Palettes / MiscTableScroll / Framework / MiscBorderView.M < prev    next >
Encoding:
Text File  |  1998-03-31  |  44.5 KB  |  1,485 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. // MiscBorderView.M
  17. //
  18. //    View class for the row/column borders on a MiscTableView.
  19. //    Supports resizing, dragging.
  20. //
  21. // NOTE: *COORDS*
  22. //    COORDINATE SYSTEM RULES:
  23. //
  24. //    1) The BorderView and the TableView must *NOT* be scaled or rotated.
  25. //        There is no reason to do so, and supporting this unnecessary
  26. //        capability is complicated and slow.  The entire TableScroll
  27. //        can be scaled and/or rotated, but the BorderView and
  28. //        TableView subviews should not be scaled and/or rotated
  29. //        relative to the TableScroll.
  30. //
  31. //    2) This means that the only coordinate transformations that are
  32. //        necessary when converting coordinates between the BorderView,
  33. //        TableView and/or TableScroll are simple translations.
  34. //
  35. //-----------------------------------------------------------------------------
  36. //-----------------------------------------------------------------------------
  37. // $Id: MiscBorderView.M,v 1.28 98/03/29 23:41:40 sunshine Exp $
  38. // $Log:    MiscBorderView.M,v $
  39. //  Revision 1.28  98/03/29  23:41:40  sunshine
  40. //  v138.1: Fixed v134.1 bug: No longer suspends/resumes editing when border
  41. //  view is used to change slot selection.  This broke relationship where one
  42. //  and only selected slot was the one containing the edit session.
  43. //  Fixed to account for new MiscBorderCell implementation which requires that
  44. //  cell's highlight flag change rather than state in order to show selection.
  45. //  Moved MISC_FRAME_HEIGHT & MISC_FRAME_WIDTH into MiscTableScrollPrivate.h
  46. //  since MiscTableBorder.cc also needs access to those values.
  47. //  
  48. //  Revision 1.27  98/03/23  07:46:13  sunshine
  49. //  v134.1: Now uses -suspendEditing / -resumeEditing.
  50. //  
  51. //  Revision 1.26  98/03/22  13:31:01  sunshine
  52. //  v133.1: Now supports resizing uniform-size borders.
  53. //  Dropped support for constrain-max.
  54. //-----------------------------------------------------------------------------
  55. #import "MiscBorderView.h"
  56. #import "MiscBorderCell.h"
  57. #import "MiscGeometry.h"
  58. #import "MiscHighlightTracker.h"
  59. #import "MiscListTracker.h"
  60. #import "MiscRadioTracker.h"
  61. #import "MiscTableBorder.h"
  62. #import "MiscTableScrollPrivate.h"
  63. #import "MiscTableView.h"
  64. #import "MiscTableWell.h"
  65. #import <MiscTableScroll/MiscTableScroll.h>
  66. #import <MiscTableScroll/MiscTableTypes.h>
  67.  
  68. #import    <new.h>
  69.  
  70. extern "Objective-C" {
  71. #import <AppKit/NSApplication.h>
  72. #import <AppKit/NSClipView.h>
  73. #import <AppKit/NSFont.h>
  74. #import <AppKit/NSCursor.h>
  75. #import <AppKit/NSImage.h>
  76. #import <AppKit/NSText.h>
  77. #import <AppKit/NSWindow.h>
  78. #import <AppKit/psopsNeXT.h>
  79. #import <Foundation/NSBundle.h>
  80. }
  81.  
  82. extern "C" {
  83. #import <float.h>
  84. #import <limits.h>
  85. #import <math.h>
  86. #import <string.h>    // memset().
  87. #import <stdio.h>    // FILENAME_MAX
  88. }
  89.  
  90. static float MIN_TOGGLE_WIDTH    = 5;
  91. static float TOGGLE_WIDTH    = 5;
  92.  
  93. int const MISC_RESIZE_EPSILON    = 4;
  94.  
  95. static NSCursor* horzCursor    = 0;
  96. static NSCursor* vertCursor    = 0;
  97. static NSCursor* dragCursor    = 0;
  98. static NSCursor* reverseCursor    = 0;
  99.  
  100. static NSImage* sortAscendImage      = 0;
  101. static NSImage* sortAscendHImage  = 0;
  102. static NSImage* sortDescendImage  = 0;
  103. static NSImage* sortDescendHImage = 0;
  104.  
  105. static BOOL sendingPeriodicEvents = NO;
  106.  
  107. static inline MiscPixels dmin( MiscPixels a, MiscPixels b )
  108.                     { return (a < b ? a : b); }
  109. static inline MiscPixels dmax( MiscPixels a, MiscPixels b )
  110.                     { return (a > b ? a : b); }
  111.  
  112.  
  113. //----------------------------------------------------------------------------
  114. // stopTimer
  115. //----------------------------------------------------------------------------
  116. static inline void stopTimer()
  117.     {
  118.     if (sendingPeriodicEvents)
  119.     {
  120.     [NSEvent stopPeriodicEvents];
  121.     sendingPeriodicEvents = NO;
  122.     }
  123.     }
  124.  
  125.  
  126. //----------------------------------------------------------------------------
  127. // startTimer
  128. //----------------------------------------------------------------------------
  129. static inline void startTimer()
  130.     {
  131.     if (!sendingPeriodicEvents)
  132.     {
  133.     sendingPeriodicEvents = YES;
  134.     [NSEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.1];
  135.     }
  136.     }
  137.  
  138.  
  139. //=============================================================================
  140. // IMPLEMENTATION
  141. //=============================================================================
  142. @implementation MiscBorderView
  143.  
  144. //-----------------------------------------------------------------------------
  145. // + imageNamed:
  146. //-----------------------------------------------------------------------------
  147. + (id)imageNamed:(NSString*)name
  148.     {
  149.     NSImage* image = [NSImage imageNamed:name];
  150.     if (image == 0)
  151.     {
  152.     NSString* path = [[NSBundle bundleForClass:self]
  153.                     pathForImageResource:name];
  154.     if (path != 0)
  155.         {
  156.         image = [[[NSImage alloc] initByReferencingFile:path] autorelease];
  157.         [image setName:name];
  158.         }
  159.     }
  160.     return image;
  161.     }
  162.  
  163.  
  164. //-----------------------------------------------------------------------------
  165. // + cursorWithImage:hot:
  166. //-----------------------------------------------------------------------------
  167. + (NSCursor*)cursorWithImage:(NSString*)name hot:(NSPoint)pt
  168.     {
  169.     NSImage* image = [self imageNamed:name];
  170.     return [[[NSCursor alloc] initWithImage:image hotSpot:pt] autorelease];
  171.     }
  172.  
  173.  
  174. //-----------------------------------------------------------------------------
  175. // + cursorWithImage:
  176. //-----------------------------------------------------------------------------
  177. + (NSCursor*)cursorWithImage:(NSString*)name
  178.     {
  179.     return [self cursorWithImage:name hot:NSMakePoint( 8, 8 )];
  180.     }
  181.  
  182.  
  183. //-----------------------------------------------------------------------------
  184. // + initialize
  185. //-----------------------------------------------------------------------------
  186. + (void)initialize
  187.     {
  188.     if (horzCursor == 0)
  189.     {
  190.     horzCursor = [[self cursorWithImage:@"MiscHorzResizeCursor"] retain];
  191.     vertCursor = [[self cursorWithImage:@"MiscVertResizeCursor"] retain];
  192.     dragCursor = [[self cursorWithImage:@"MiscSlotDragCursor"] retain];
  193.     reverseCursor = [[self cursorWithImage:@"MiscReverseCursor"
  194.             hot:NSMakePoint( 4, 0 )] retain];
  195.  
  196.     sortAscendImage   = [[self imageNamed:@"MiscSortAscend"] retain];
  197.     sortAscendHImage  = [[self imageNamed:@"MiscSortAscendH"] retain];
  198.     sortDescendImage  = [[self imageNamed:@"MiscSortDescend"] retain];
  199.     sortDescendHImage = [[self imageNamed:@"MiscSortDescendH"] retain];
  200.  
  201.     TOGGLE_WIDTH = [sortAscendImage size].width;
  202.     if (TOGGLE_WIDTH < MIN_TOGGLE_WIDTH)
  203.         TOGGLE_WIDTH = MIN_TOGGLE_WIDTH;
  204.     }
  205.     }
  206.  
  207.  
  208. //-----------------------------------------------------------------------------
  209. // TYPE VARIATIONS
  210. //-----------------------------------------------------------------------------
  211.  
  212. - (MiscPixels)frameHeight
  213.     {
  214.     NSRect rect = [self frame];
  215.     return (MiscPixels) (isHorz ? rect.size.height : rect.size.width);
  216.     }
  217.  
  218. - (void)setFrameHeight:(MiscPixels)x
  219.     {
  220.     NSRect rect = [self frame];
  221.     if (isHorz)
  222.     rect.size.height = x;
  223.     else
  224.     rect.size.width = x;
  225.     [self setFrame:rect];
  226.     }
  227.  
  228. - (NSCursor*)cursor
  229.     { return isHorz ? horzCursor : vertCursor; }
  230. - (MiscBorderType)borderType
  231.     { return isHorz ? MISC_COL_BORDER : MISC_ROW_BORDER; }
  232. - (MiscTableBorder*)otherBorder
  233.     { return [scroll border:MISC_OTHER_BORDER([self borderType])]; }
  234. - (void)clearOtherBorder { [self otherBorder]->selectNone(); }
  235. - (id)cellAtRow:(MiscCoord_P)row column:(MiscCoord_P)col
  236.     { return isHorz ?   [scroll cellAtRow:row column:col] :
  237.             [scroll cellAtRow:col column:row]; }
  238.  
  239.  
  240. //-----------------------------------------------------------------------------
  241. // - changeFrameIfNeeded
  242. //    FIXME: The frame updates need to be untangled.  All requests should
  243. //    go through the table-scroll object.  Implementations should only
  244. //    update themselves, not other objects.
  245. //-----------------------------------------------------------------------------
  246. - (void)changeFrameIfNeeded
  247.     {
  248.     MiscRect_O r( isHorz, [self frame] );
  249.     MiscPixels const my_width = r.getWidth_O();
  250.     MiscPixels const i_width = info->totalSize();
  251.     if (my_width != i_width)
  252.     {
  253.     [[self window] invalidateCursorRectsForView:self];
  254.     r.setWidth_O( i_width );
  255.     [self setFrameSize:r];
  256.     [[scroll documentView] adjustSize];
  257.     }
  258.     }
  259.  
  260.  
  261. //-----------------------------------------------------------------------------
  262. // - setPos:width:
  263. //-----------------------------------------------------------------------------
  264. - (void)setPos:(MiscCoord_V)pos width:(MiscPixels)size
  265.     {
  266.     MiscCoord_P pPos = info->visualToPhysical(pos);
  267.     [scroll border:[self borderType] setSlot:pPos size:(float)size];
  268.     [[self window] invalidateCursorRectsForView:self];
  269.     [scroll setNeedsDisplay:YES];
  270.     }
  271.  
  272.  
  273. //-----------------------------------------------------------------------------
  274. // - initWithFrame:scroll:info:type:
  275. //-----------------------------------------------------------------------------
  276. - (id)initWithFrame:(NSRect)frameRect
  277.      scroll:(MiscTableScroll*)i_scroll
  278.        info:(MiscTableBorder*)i_info
  279.        type:(MiscBorderViewType)type
  280.     {
  281.     NSZone* const z = [self zone];
  282.  
  283.     [super initWithFrame:NSZeroRect];
  284.  
  285.     togglePos = -1;
  286.     isHorz = (type == MISC_COL_BORDER_VIEW);
  287.     scroll = i_scroll;
  288.     info = i_info;
  289.     theCell = [[MiscBorderCell allocWithZone:z] initTextCell:@"Kilroy"];
  290.  
  291.     oldSel = new( NSZoneMalloc(z,sizeof(*oldSel)) ) MiscSparseSet;
  292.     [self setSelectionMode:[scroll selectionMode]];
  293.  
  294.     MiscRect_O myFrame( isHorz, frameRect );
  295.     myFrame.setWidth_O( i_info->totalSize() );
  296.     myFrame.setHeight_O( (isHorz ? MISC_FRAME_HEIGHT : MISC_FRAME_WIDTH) );
  297.     [self setFrame:myFrame];
  298.     return self;
  299.     }
  300.  
  301.  
  302. //-----------------------------------------------------------------------------
  303. // - dealloc
  304. //-----------------------------------------------------------------------------
  305. - (void)dealloc
  306.     {
  307.     [theCell release];
  308.     [tracker release];
  309.     if (oldSel != 0)
  310.     {
  311.     oldSel->MiscSparseSet::~MiscSparseSet();
  312.     NSZoneFree( [self zone], oldSel );
  313.     }
  314.     [super dealloc];
  315.     }
  316.  
  317.  
  318. //-----------------------------------------------------------------------------
  319. // - isFlipped
  320. //-----------------------------------------------------------------------------
  321. - (BOOL)isFlipped
  322.     {
  323.     return YES;
  324.     }
  325.  
  326.  
  327. //-----------------------------------------------------------------------------
  328. // - isOpaque
  329. //-----------------------------------------------------------------------------
  330. - (BOOL)isOpaque
  331.     {
  332.     return YES;
  333.     }
  334.  
  335.  
  336.  
  337. //=============================================================================
  338. // CONVERSIONS
  339. //=============================================================================
  340. //-----------------------------------------------------------------------------
  341. // - range::fromRect:
  342. //    Returns by reference the range of visual slots contained in rect.
  343. //    rMin is inclusive, rMax is exclusive.
  344. //-----------------------------------------------------------------------------
  345. - (void)range:(MiscCoord_V*)rMin :(MiscCoord_V*)rMax fromRect:(NSRect)rect
  346.     {
  347.     MiscRect_O r( isHorz, rect );
  348.     *rMin = info->visualForOffset( r.getX_O() );
  349.     *rMax = info->visualForOffset( r.getMaxX_O() - 1 ) + 1;
  350.     }
  351.  
  352.  
  353. //-----------------------------------------------------------------------------
  354. // - rectForPos:
  355. //-----------------------------------------------------------------------------
  356. - (NSRect)rectForPos:(MiscCoord_V)pos
  357.     {
  358.     MiscRect_O r( isHorz, [self bounds] );
  359.     r.setX_O( info->getOffset(pos) );
  360.     r.setWidth_O( info->effectiveSize(pos) );
  361.     return r;
  362.     }
  363.  
  364.  
  365.  
  366. //=============================================================================
  367. // DRAWING
  368. //=============================================================================
  369. //-----------------------------------------------------------------------------
  370. // - getVisibleRange::
  371. //
  372. //    Returns by reference the range of visual slots contained in the visible
  373. //    rectangle.  rMin is inclusive, rMax is exclusive.
  374. //-----------------------------------------------------------------------------
  375. - (void)getVisibleRange:(MiscCoord_V*)rMin :(MiscCoord_V*)rMax
  376.     {
  377.     id v = [self superview];
  378.     NSRect visRect = [v documentVisibleRect];
  379.     [self range:rMin:rMax fromRect:visRect];
  380.     }
  381.  
  382.  
  383. //-----------------------------------------------------------------------------
  384. // drewPos:
  385. //
  386. //    Keeps the old selection sets up to date whenever we draw a cell.  This
  387. //    way -selectionChanged has valid data to work from.
  388. //-----------------------------------------------------------------------------
  389. - (void)drewPos:(MiscCoord_V)slot
  390.     {
  391.     BOOL isSelected = info->isSelected( slot );
  392.     if (isSelected != oldSel->contains(slot))
  393.     {
  394.     if (isSelected)
  395.         oldSel->add( slot );
  396.     else
  397.         oldSel->remove( slot );
  398.     }
  399.     }
  400.  
  401.  
  402. //-----------------------------------------------------------------------------
  403. // - drawPos:inRect:controlView:
  404. //-----------------------------------------------------------------------------
  405. - (void)drawPos:(MiscCoord_V)pos inRect:(NSRect)r controlView:(NSView*)v
  406.     {
  407.     NSImage* img = 0;
  408.     if ([scroll autoSortSlots:MISC_OTHER_BORDER([self borderType])])
  409.     if (info->isSortable(pos))
  410.         if (info->getSortDirection(pos) == MISC_SORT_DESCENDING)
  411.         img = togglePos == pos ? sortDescendHImage : sortDescendImage;
  412.         else
  413.         img = togglePos == pos ? sortAscendHImage : sortAscendImage;
  414.  
  415.     [theCell setToggleImage:img];
  416.     [theCell setStringValue:info->getTitle(pos)];
  417.  
  418.     BOOL const need_off = !info->isSelected(pos); // Forces "real" boolean
  419.     BOOL const is_off = ![theCell isHighlighted]; // in order to compare them.
  420.     if (need_off != is_off)
  421.     [theCell highlight:!need_off withFrame:r inView:v];
  422.     else
  423.     [theCell drawWithFrame:r inView:v];
  424.     }
  425.  
  426.  
  427. //-----------------------------------------------------------------------------
  428. // - drawPos:updateSel:
  429. //-----------------------------------------------------------------------------
  430. - (void)drawPos:(MiscCoord_V)pos updateSel:(BOOL)updateSel
  431.     {
  432.     if (0 <= pos && pos < info->count())
  433.     {
  434.     NSRect rect = [self rectForPos:pos];
  435.     [self drawPos:pos inRect:rect controlView:self];
  436.     if (updateSel)
  437.         [self drewPos:pos];
  438.     }
  439.     }
  440.  
  441.  
  442. //-----------------------------------------------------------------------------
  443. // - drawPos:
  444. //-----------------------------------------------------------------------------
  445. - (void)drawPos:(MiscCoord_V)pos
  446.     {
  447.     [self drawPos:pos updateSel:YES];
  448.     }
  449.  
  450.  
  451. //-----------------------------------------------------------------------------
  452. // - drawRect:
  453. //-----------------------------------------------------------------------------
  454. - (void)drawRect:(NSRect)rect
  455.     {
  456.     MiscCoord_V pos_min;
  457.     MiscCoord_V pos_max;
  458.     [self range:&pos_min:&pos_max fromRect:rect];
  459.     for (MiscCoord_V pos = pos_min;  pos < pos_max;  pos++)
  460.     [self drawPos:pos];
  461.     }
  462.  
  463.  
  464. //-----------------------------------------------------------------------------
  465. // - drawSlot:    [public]
  466. //-----------------------------------------------------------------------------
  467. - (void)drawSlot:(MiscCoord_V)n
  468.     {
  469.     MiscCoord_V rMin, rMax;
  470.     [self getVisibleRange:&rMin:&rMax];
  471.     if (rMin <= n && n < rMax)
  472.     [self setNeedsDisplayInRect:[self rectForPos:n]];
  473.     }
  474.  
  475.  
  476.  
  477. //=============================================================================
  478. // SELECTION
  479. //=============================================================================
  480. //-----------------------------------------------------------------------------
  481. // - setSelectionMode:
  482. //-----------------------------------------------------------------------------
  483. - (void)setSelectionMode:(MiscSelectionMode)mode
  484.     {
  485.     NSZone* const z = [self zone];
  486.     if (tracker)
  487.     [tracker release];
  488.     switch (mode)
  489.     {
  490.     case MISC_LIST_MODE:
  491.         tracker = [MiscListTracker allocWithZone:z];
  492.         break;
  493.     case MISC_RADIO_MODE:
  494.         tracker = [MiscRadioTracker allocWithZone:z];
  495.         break;
  496.     case MISC_HIGHLIGHT_MODE:
  497.         tracker = [MiscHighlightTracker allocWithZone:z];
  498.         break;
  499.     }
  500.     [tracker initBorder:info];
  501.     }
  502.  
  503.  
  504. //-----------------------------------------------------------------------------
  505. // - selectionChanged
  506. //-----------------------------------------------------------------------------
  507. - (void)selectionChanged
  508.     {
  509.     MiscSparseSet const& newSel = info->selectionSet();
  510.  
  511.     MiscCoord_V rMin, rMax;
  512.     [self getVisibleRange:&rMin:&rMax];
  513.  
  514.     for (MiscCoord_V i = rMin;  i < rMax;  i++)
  515.     if (oldSel->contains(i) != newSel.contains(i))
  516.         [self setNeedsDisplayInRect:[self rectForPos:i]];
  517.  
  518.     *oldSel = newSel;
  519.     }
  520.  
  521.  
  522. //-----------------------------------------------------------------------------
  523. // - resetSelection
  524. //-----------------------------------------------------------------------------
  525. - (void)resetSelection
  526.     {
  527.     *oldSel = info->selectionSet();
  528.     }
  529.  
  530.  
  531. //-----------------------------------------------------------------------------
  532. // - selectPos:
  533. //-----------------------------------------------------------------------------
  534. - (void)selectPos:(MiscCoord_V)pos
  535.     {
  536.     info->selectOne( pos );
  537.     [scroll selectionChanged];
  538.     }
  539.  
  540.  
  541.  
  542. //=============================================================================
  543. // CURSOR MANAGEMENT
  544. //=============================================================================
  545. //-----------------------------------------------------------------------------
  546. // - toggleRect:forPos:visible:
  547. //-----------------------------------------------------------------------------
  548. - (BOOL)toggleRect:(NSRect*)nsrect forPos:(MiscCoord_V)pos visible:(NSRect)vis
  549.     {
  550.     MiscPixels const pos_width = info->effectiveSize(pos);
  551.     if (pos_width > 0)
  552.     {
  553.     float const w = TOGGLE_WIDTH + MISC_RESIZE_EPSILON;
  554.     *nsrect = [self rectForPos:pos];
  555.     nsrect->origin.x += nsrect->size.width - w;
  556.     nsrect->size.width = w;
  557.     *nsrect = NSIntersectionRect( vis, *nsrect );
  558.     return !NSIsEmptyRect( *nsrect );
  559.     }
  560.     return NO;
  561.     }
  562.  
  563. //-----------------------------------------------------------------------------
  564. // - resizeRect:forPos:visible:
  565. //-----------------------------------------------------------------------------
  566. - (BOOL)resizeRect:(NSRect*)nsrect forPos:(MiscCoord_V)pos visible:(NSRect)vis
  567.     {
  568.     MiscPixels const pos_width = info->effectiveSize(pos);
  569.     if (pos_width > 0)
  570.     {
  571.     MiscPixels const pos_offset = info->getOffset(pos);
  572.     MiscPixels const min_x = pos_offset + pos_width - MISC_RESIZE_EPSILON;
  573.     MiscRect_O r( isHorz, [self bounds] );
  574.     r.setX_O( min_x );
  575.     r.setWidth_O( MISC_RESIZE_EPSILON );
  576.     *nsrect = NSIntersectionRect( vis, r );
  577.     return !NSIsEmptyRect( *nsrect );
  578.     }
  579.     return NO;
  580.     }
  581.  
  582.  
  583. //-----------------------------------------------------------------------------
  584. // - resetCursorRects
  585. //-----------------------------------------------------------------------------
  586. - (void)resetCursorRects
  587.     {
  588.     BOOL const draggable = info->isDraggable();
  589.     BOOL const sizeable = info->isSizeable();
  590.     NSRect vis = [self visibleRect];
  591.  
  592.     if (draggable && !info->isModifierDrag())
  593.     [self addCursorRect:vis cursor:dragCursor];
  594.  
  595.     if ([scroll autoSortSlots:MISC_OTHER_BORDER([self borderType])])
  596.     {
  597.     MiscCoord_V min_pos, max_pos;
  598.     [self getVisibleRange:&min_pos:&max_pos];
  599.     int const count = info->count();
  600.     if (0 <= min_pos && min_pos < count)
  601.         {
  602.         NSRect rect;
  603.         int const lim = (max_pos <= count) ? max_pos : count;
  604.         for (MiscCoord_V pos = min_pos; pos < lim; pos++)
  605.         if (info->isSortable(pos) &&
  606.             [self toggleRect:&rect forPos:pos visible:vis])
  607.             [self addCursorRect:rect cursor:reverseCursor];
  608.         }
  609.     }
  610.  
  611.     if (sizeable)
  612.     {
  613.     BOOL const uniform = info->isUniformSize();
  614.     NSCursor* const resizeCursor = [self cursor];
  615.     MiscCoord_V min_pos, max_pos;
  616.     [self getVisibleRange:&min_pos:&max_pos];
  617.     int const count = info->count();
  618.     if (0 <= min_pos && min_pos < count)
  619.         {
  620.         NSRect rect;
  621.         int const lim = (max_pos <= count) ? max_pos : count;
  622.         for (MiscCoord_V pos = min_pos; pos < lim; pos++)
  623.         if ((uniform || info->isSizeable(pos)) &&
  624.             [self resizeRect:&rect forPos:pos visible:vis])
  625.             [self addCursorRect:rect cursor:resizeCursor];
  626.         }
  627.     }
  628.     }
  629.  
  630.  
  631.  
  632. //=============================================================================
  633. // MOUSE-TRACKING
  634. //=============================================================================
  635. //-----------------------------------------------------------------------------
  636. // - posForMousePt:
  637. //-----------------------------------------------------------------------------
  638. - (MiscCoord_V)posForMousePt:(NSPoint)p
  639.     {
  640.     MiscPixels pix = MiscPoint_O( isHorz, p ).getX_O();
  641.     MiscRect_O r( isHorz, [self bounds] );
  642.     return (pix < r.getMaxX_O() ? info->visualForOffset(pix) : info->count());
  643.     }
  644.  
  645.  
  646.  
  647. //=============================================================================
  648. // RESIZING
  649. //=============================================================================
  650. //-----------------------------------------------------------------------------
  651. // - resizeEvent:x:deltaX:minX:maxX:
  652. //-----------------------------------------------------------------------------
  653. - (int)resizeEvent:(NSEvent*)p
  654.     x:(MiscPixels)x
  655.     deltaX:(MiscPixels)deltaX
  656.     minX:(MiscPixels)minX
  657.     maxX:(MiscPixels)maxX
  658.     {
  659.     NSRect nsDocFrame = [scroll documentClipRect];
  660.     NSRect nsClipFrame = [[self superview] frame];
  661.     MiscRect_O docFrame( isHorz, nsDocFrame );
  662.     MiscRect_O clipFrame( isHorz, nsClipFrame );
  663.     MiscPixels const minDrawX = clipFrame.getX_O();    // Scroll coords.
  664.     MiscPixels const maxDrawX = clipFrame.getMaxX_O();    // Scroll coords.
  665.     unsigned int const WANTED =
  666.         (NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSPeriodicMask);
  667.  
  668.     x += deltaX;
  669.  
  670.     MiscPixels draw_x;                    // Scroll coords.
  671.     MiscRect_O line( isHorz );
  672.     line.setX_O( x - 1 );
  673.     line.setY_O( 0 );
  674.     line.setWidth_O( 2 );
  675.     line.setHeight_O( clipFrame.getHeight_O() + docFrame.getHeight_O() - 1 );
  676.     line = [scroll convertPoint:line fromView:self];
  677.     draw_x = line.getX_O();
  678.  
  679.     BOOL did_scroll = NO;
  680.     BOOL in_bounds = (minDrawX <= draw_x && draw_x <= maxDrawX);
  681.  
  682.     [scroll lockFocus];
  683.     PSsetinstance(YES);
  684.     [[NSColor blackColor] set];
  685.  
  686.     if (in_bounds)
  687.     NSRectFill( line );
  688.  
  689.     startTimer();
  690.     NSEvent* lastEvent = [p copy];
  691.  
  692.     for (;;)
  693.     {
  694.     p = [[self window] nextEventMatchingMask:WANTED];
  695.  
  696.     if (p == 0 || [p type] == NSLeftMouseUp)
  697.         break;
  698.     else if ([p type] == NSPeriodic)
  699.         {
  700.         NSPoint mousePt =
  701.         [scroll convertPoint:[lastEvent locationInWindow] fromView:0];
  702.         MiscPixels const mousePtX = MiscPoint_O( isHorz,mousePt ).getX_O();
  703.         if (mousePtX < minDrawX || maxDrawX < mousePtX)
  704.         {
  705.         PSsetinstance(NO);
  706.         [self autoscroll:lastEvent];
  707.         PSsetinstance(YES);
  708.         did_scroll = YES;
  709.         }
  710.         }
  711.     else
  712.         {
  713.         [lastEvent release];
  714.         lastEvent = [p copy];
  715.         }
  716.  
  717.     NSPoint new_loc =
  718.         [self convertPoint:[lastEvent locationInWindow] fromView:0];
  719.     MiscPixels mouseX = MiscPoint_O( isHorz, new_loc ).getX_O();
  720.     MiscPixels new_x = (mouseX + deltaX);
  721.     if (new_x < minX)
  722.         new_x = minX;
  723.     else if (new_x >= maxX)
  724.         new_x = maxX - 1;
  725.  
  726.     BOOL const did_move = (new_x != x);
  727.     if (did_move || did_scroll)
  728.         {
  729.         if (in_bounds && !did_scroll)
  730.         PShideinstance( line.getX(), line.getY(),
  731.                 line.getWidth(), line.getHeight() );
  732.         if (did_move)
  733.         {
  734.         x = new_x;
  735.         line.setX_O( x - 1 );
  736.         line.setY_O( 0 );
  737.         line = [scroll convertPoint:line fromView:self];
  738.         draw_x = line.getX_O();
  739.         in_bounds = (minDrawX <= draw_x && draw_x <= maxDrawX);
  740.         }
  741.  
  742.         if (in_bounds)
  743.         NSRectFill( line );
  744.  
  745.         did_scroll = NO;
  746.         }
  747.     }
  748.  
  749.     [lastEvent release];
  750.     stopTimer();
  751.  
  752.     if (in_bounds)
  753.     PShideinstance( line.getX(), line.getY(),
  754.                 line.getWidth(), line.getHeight() );
  755.  
  756.     PSsetinstance(NO);
  757.     [scroll unlockFocus];
  758.  
  759.     x -= deltaX;
  760.     return x;
  761.     }
  762.  
  763.  
  764. //-----------------------------------------------------------------------------
  765. // - resizeEvent:inPos:atX:deltaX:finalWidth:
  766. //-----------------------------------------------------------------------------
  767. - (BOOL)resizeEvent:(NSEvent*)p
  768.      inPos:(MiscCoord_V)pos
  769.      atX:(MiscPixels)x_origin
  770.      deltaX:(MiscPixels)delta_x
  771.      finalWidth:(MiscPixels*)finalWidth
  772.     {
  773.     MiscPixels const org_x = info->getOffset(pos);
  774.     MiscPixels const min_x = org_x + info->effectiveMinSize(pos);
  775.     MiscPixels const max_x = org_x + info->getMaxSize(pos) + 1;
  776.     MiscPixels const curr_x =
  777.     [self resizeEvent:p x:x_origin deltaX:delta_x minX:min_x maxX:max_x];
  778.     MiscPixels final_delta = curr_x - x_origin;
  779.     if (final_delta != 0)
  780.     {
  781.     *finalWidth = info->effectiveSize(pos) + final_delta;
  782.     return YES;
  783.     }
  784.  
  785.     return NO;
  786.     }
  787.  
  788.  
  789. //-----------------------------------------------------------------------------
  790. // - inResizeZone:forPos:atX:deltaX:
  791. //-----------------------------------------------------------------------------
  792. - (BOOL)inResizeZone:(NSPoint)pt
  793.     forPos:(MiscCoord_V*)pos
  794.     atX:(MiscPixels*)pos_x
  795.     deltaX:(MiscPixels*)delta_x
  796.     {
  797.     if (info->isSizeable())
  798.     {
  799.     MiscCoord_V const plim = info->count();
  800.     MiscPixels x = MiscPoint_O( isHorz, pt ).getX_O();
  801.     MiscCoord_V p = info->visualForOffset(x);
  802.     if (0 <= p && p < plim)
  803.         {
  804.         BOOL const uniform = info->isUniformSize();
  805.         MiscPixels max_x = info->getOffset(p) + info->effectiveSize(p);
  806.         MiscPixels delta = max_x - x;
  807.         if (0 <= delta && delta <= MISC_RESIZE_EPSILON)
  808.         {
  809.         do { p++; } while (p < plim && info->effectiveSize(p) <= 0);
  810.         p--;
  811.         if (0 <= p && p < plim && (uniform || info->isSizeable(p)))
  812.             {
  813.             *pos = p;
  814.             *pos_x = x;
  815.             *delta_x = delta;
  816.             return YES;
  817.             }
  818.         }
  819.         }
  820.     }
  821.     return NO;
  822.     }
  823.  
  824.  
  825. //-----------------------------------------------------------------------------
  826. // - adjustSize
  827. //-----------------------------------------------------------------------------
  828. - (void)adjustSize
  829.     {
  830.     [self changeFrameIfNeeded];
  831.     }
  832.  
  833.  
  834.  
  835. //=============================================================================
  836. // DRAGGING
  837. //=============================================================================
  838. //-----------------------------------------------------------------------------
  839. // draw_view
  840. //    Draw a view hierarchy without calling lockFocus so that it can be
  841. //    drawn onto a different window.  Used for drawing the drag cache
  842. //    window.
  843. //-----------------------------------------------------------------------------
  844. static void draw_view( NSView* v, NSRect r )
  845.     {
  846.     // FIXME: maybe clip, maybe manually transform graphics state coord matrix.
  847.     [v drawRect:r];
  848.     NSArray* subs = [v subviews];
  849.     unsigned int lim = (subs ? [subs count] : 0);
  850.     for (unsigned int i = 0; i < lim; i++)
  851.     {
  852.     NSView* sub = (NSView*)[subs objectAtIndex:i];
  853.     NSRect subFrame = NSIntersectionRect( r, [sub frame] );
  854.     if (!NSIsEmptyRect( subFrame ))
  855.         {
  856.         subFrame = [sub convertRect:subFrame fromView:v];
  857.         draw_view( sub, subFrame );
  858.         }
  859.     }
  860.     }
  861.  
  862.  
  863. //-----------------------------------------------------------------------------
  864. // - dragCacheForPos:
  865. //
  866. // NOTE *COORDS*
  867. //    The TableScroll, BorderView, and TableView all share the the same 
  868. //    scaling and rotation.  Also, the coordinate system of the TableView 
  869. //    is always anchored at the same "X" offset as the BorderView, so that 
  870. //    (orientation corrected) "X" coordinates and widths can be exchanged 
  871. //    between the two views without further adjustments.  
  872. //-----------------------------------------------------------------------------
  873. - (NSImage*)dragCacheForPos:(MiscCoord_V)pos
  874.     {
  875.     NSImage* cache = [[NSImage allocWithZone:[self zone]] init];
  876.     MiscTableView* tableView = (MiscTableView*) [scroll documentView];
  877.  
  878.     NSRect nsBorder = [self rectForPos:pos];
  879.     NSRect nsTable = [tableView visibleRect];
  880.  
  881.     MiscRect_O oBorder( isHorz, nsBorder );
  882.     MiscRect_O oTable( isHorz, nsTable );
  883.  
  884.     MiscSize_O oCache( isHorz );
  885.     oCache.setWidth_O( oBorder.getWidth_O() );
  886.     oCache.setHeight_O( oBorder.getHeight_O() + oTable.getHeight_O() );
  887.     NSSize nsCache = oCache;
  888.     [cache setSize:nsCache];
  889.  
  890.     if (oTable.getX_O() <= oBorder.getX_O() &&        // Is entire slot
  891.     oBorder.getMaxX_O() <= oTable.getMaxX_O())    // visible?
  892.     {
  893.     MiscRect_O oScroll( isHorz );
  894.     oScroll.setX_O( oBorder.getX_O() );
  895.     oScroll.setY_O( oBorder.getY_O() );
  896.     oScroll.setWidth_O( oBorder.getWidth_O() );
  897.     oScroll.setHeight_O( oCache.getHeight_O() );
  898.     NSRect nsScroll = [scroll convertRect:oScroll fromView:self];
  899.  
  900.     [scroll lockFocus];
  901.     NSBitmapImageRep* rep = [[[NSBitmapImageRep allocWithZone:[self zone]]
  902.                 initWithFocusedViewRect:nsScroll] autorelease];
  903.     [scroll unlockFocus];
  904.     [cache addRepresentation:rep];
  905.     }
  906.     else
  907.     {
  908.     [cache setFlipped:YES];
  909.     [cache lockFocus];
  910.  
  911.     oTable.setX_O( oBorder.getX_O() );        // NOTE *COORDS*
  912.     oTable.setWidth_O( oBorder.getWidth_O() );
  913.     nsTable = oTable;
  914.  
  915.     MiscPoint_O oDelta( isHorz, nsTable.origin );
  916.     oDelta.setY_O( oDelta.getY_O() - oBorder.getHeight_O() );
  917.     NSPoint delta = oDelta;
  918.  
  919.     NSView* focusView = [NSView focusView];
  920.  
  921.     [focusView translateOriginToPoint:NSMakePoint( -delta.x, -delta.y )];
  922.     draw_view( tableView, nsTable );
  923.     [focusView translateOriginToPoint:NSMakePoint( delta.x, delta.y )];
  924.  
  925.     [focusView translateOriginToPoint:
  926.             NSMakePoint( -nsBorder.origin.x, -nsBorder.origin.y )];
  927.     draw_view( self, nsBorder );
  928.     [focusView translateOriginToPoint:
  929.             NSMakePoint( nsBorder.origin.x, nsBorder.origin.y )];
  930.  
  931.     [cache unlockFocus];
  932.     }
  933.  
  934.     return [cache autorelease];
  935.     }
  936.  
  937.  
  938. //-----------------------------------------------------------------------------
  939. // - getVisibleCacheMin:max:
  940. //-----------------------------------------------------------------------------
  941. - (NSImage*)getVisibleCacheMin:(MiscPixels*)pMin max:(MiscPixels*)pMax
  942.     {
  943.     MiscRect_O rDoc( isHorz, [[scroll documentView] visibleRect] );
  944.     MiscRect_O rVis( isHorz, [self visibleRect] );
  945.     *pMin = rVis.getX_O();
  946.     *pMax = rVis.getMaxX_O();
  947.     rDoc = [scroll convertRect:rDoc fromView:[scroll documentView]];
  948.     rVis = [scroll convertRect:rVis fromView:self];
  949.  
  950.     MiscRect_O r( isHorz );
  951.     r.setX_O( rVis.getX_O() );
  952.     r.setY_O( rVis.getY_O() );
  953.     r.setWidth_O( rVis.getWidth_O() );
  954.     r.setHeight_O( rVis.getHeight_O() + rDoc.getHeight_O() );
  955.  
  956.     [scroll lockFocus];
  957.     NSBitmapImageRep* rep = [[NSBitmapImageRep allocWithZone:[self zone]]
  958.                 initWithFocusedViewRect:r];
  959.     [scroll unlockFocus];
  960.  
  961.     NSImage* cache = [[NSImage allocWithZone:[self zone]] initWithSize:r];
  962.     [cache addRepresentation:rep];
  963.     return [cache autorelease];
  964.     }
  965.  
  966.  
  967. //-----------------------------------------------------------------------------
  968. // - setWells::forPos:
  969. //-----------------------------------------------------------------------------
  970. - (void)setWells:(MiscTableWell**)w1 :(MiscTableWell**)w2
  971.     forPos:(MiscCoord_V)pos
  972.     {
  973.     NSZone* const z = [self zone];
  974.     MiscTableView* doc = [scroll documentView];
  975.     MiscRect_O rDoc( isHorz, [doc visibleRect] );
  976.     MiscRect_O rClip( isHorz, [[self superview] frame] );
  977.  
  978.     MiscRect_O r( isHorz );
  979.     r.setX_O( info->getOffset(pos) );
  980.     r.setWidth_O( info->effectiveSize(pos) );
  981.     r.setHeight_O( rClip.getHeight_O() );
  982.  
  983.     *w1 = [[MiscTableWell allocWithZone:z] initWithFrame:r];
  984.     [self addSubview:*w1];
  985.  
  986.     r.setY_O( rDoc.getY_O() );
  987.     r.setHeight_O( rDoc.getHeight_O() );
  988.     *w2 = [[MiscTableWell allocWithZone:z] initWithFrame:r];
  989.     [doc addSubview:*w2];
  990.  
  991.     [*w1 display];
  992.     [*w2 display];
  993.     }
  994.  
  995.  
  996. //-----------------------------------------------------------------------------
  997. // - clearWells::
  998. //-----------------------------------------------------------------------------
  999. - (void)clearWells:(MiscTableWell**)w1 :(MiscTableWell**)w2
  1000.     {
  1001.     [*w1 removeFromSuperview];
  1002.     [*w2 removeFromSuperview];
  1003.     [*w1 release];
  1004.     [*w2 release];
  1005.     *w1 = 0;
  1006.     *w2 = 0;
  1007.     // NOTE: Does not need display here.  Everything will get displayed later.
  1008.     }
  1009.  
  1010.  
  1011. //-----------------------------------------------------------------------------
  1012. // - offsetFromEvent:
  1013. //-----------------------------------------------------------------------------
  1014. - (float)offsetFromEvent:(NSEvent*)ev 
  1015.     {
  1016.     NSPoint mLoc = [self convertPoint:[ev locationInWindow] fromView:0];
  1017.     return MiscPoint_O( isHorz, mLoc ).getX_O();
  1018.     }
  1019.  
  1020.  
  1021. //-----------------------------------------------------------------------------
  1022. // - calcDrop:::
  1023. //-----------------------------------------------------------------------------
  1024. - (MiscCoord_V)calcDrop:(MiscCoord_V)fromPos
  1025.             :(NSPoint)mouseDownPt
  1026.             :(NSPoint)mouseUpPt
  1027.     {
  1028.     MiscCoord_V toPos = fromPos;
  1029.     MiscPixels const start_pos = MiscPoint_O( isHorz, mouseDownPt ).getX_O();
  1030.     MiscPixels const end_pos   = MiscPoint_O( isHorz, mouseUpPt   ).getX_O();
  1031.     MiscPixels const delta_pos = (end_pos - start_pos);
  1032.  
  1033.     MiscPixels const SLOP = 4;
  1034.     if (delta_pos < -SLOP || SLOP < delta_pos)
  1035.     {
  1036.     MiscPixels const start_ofs = info->getOffset( fromPos );
  1037.     MiscPixels drop_pos = start_ofs + delta_pos;
  1038.     if (delta_pos < 0)
  1039.         drop_pos += SLOP;
  1040.     else
  1041.         drop_pos += info->effectiveSize(fromPos) - SLOP;
  1042.     toPos = info->visualForOffset( drop_pos );
  1043.     if (toPos < 0)
  1044.         toPos = 0;
  1045.     }
  1046.  
  1047.     return toPos;
  1048.     }
  1049.  
  1050.  
  1051. //-----------------------------------------------------------------------------
  1052. // - dragEvent:inPos:
  1053. //-----------------------------------------------------------------------------
  1054. - (MiscCoord_V)dragEvent:(NSEvent*)event inPos:(MiscCoord_V)pos
  1055.     {
  1056.     int const WANTED =
  1057.         (NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSPeriodicMask);
  1058.  
  1059.     NSPoint mouseDownPt =
  1060.         [self convertPoint:[event locationInWindow] fromView:0];
  1061.     NSPoint mouseUpPt = mouseDownPt;
  1062.     NSWindow* win = [self window];
  1063.  
  1064.     [scroll disableCursor];
  1065.     [win flushWindow];
  1066.     [win disableFlushWindow];
  1067.  
  1068.     NSImage* dragCache = [[self dragCacheForPos:pos] retain];
  1069.  
  1070.     MiscTableWell *w1, *w2;
  1071.     [self setWells:&w1 :&w2 forPos:pos];
  1072.  
  1073.     MiscPixels pMin,pMax;
  1074.     NSImage* visCache = [[self getVisibleCacheMin:&pMin max:&pMax] retain];
  1075.  
  1076.     MiscSize_O size( isHorz, [dragCache size] );
  1077.     MiscPixels pLoc = info->getOffset( pos );
  1078.     MiscPixels delta = MiscPixels( [self offsetFromEvent:event] - pLoc );
  1079.  
  1080.     NSEvent* lastEvent = [event copy];
  1081.     [scroll lockFocus];
  1082.     for (;;)
  1083.     {
  1084.     MiscPoint_O pt( isHorz );
  1085.     MiscRect_O rDrag( isHorz );
  1086.     MiscPixels const w = size.getWidth_O();
  1087.     MiscPixels const dw =
  1088.         dmin( dmin( dmin(w, pMax - pMin), pMax - pLoc), pLoc + w - pMin );
  1089.     BOOL const shouldDraw = (dw > 0);
  1090.     if (shouldDraw)
  1091.         {
  1092.         if (isHorz)
  1093.         {
  1094.         pt.setX( dmax( pLoc, pMin ) );
  1095.         pt.setY( size.getHeight_O() );
  1096.         if (pLoc < pMin)
  1097.             rDrag.setX( pMin - pLoc );
  1098.         }
  1099.         else
  1100.         {
  1101.         pt.setX( 0 );
  1102.         pt.setY( dmax( pLoc, pMin ) + dw );
  1103.         if (pLoc + w >= pMax)
  1104.             rDrag.setY( pLoc + w - pMax );
  1105.         }
  1106.         pt = [scroll convertPoint:pt fromView:self];
  1107.         rDrag.setWidth_O( dw );
  1108.         rDrag.setHeight_O( size.getHeight_O() );
  1109.         [dragCache compositeToPoint:pt fromRect:rDrag
  1110.             operation:NSCompositeCopy];
  1111.         }
  1112.     [win enableFlushWindow];
  1113.     [win flushWindow];
  1114.  
  1115.     event = [[self window] nextEventMatchingMask:WANTED];
  1116.  
  1117.     [win disableFlushWindow];
  1118.     if (shouldDraw)
  1119.         {
  1120.         NSSize s = [visCache size];
  1121.         MiscPixels xTarg;
  1122.         if (isHorz)
  1123.         {
  1124.         xTarg = (pLoc < pMin ? 0 : pLoc - pMin);
  1125.         }
  1126.         else
  1127.         {
  1128.         if (pLoc < pMin)
  1129.             xTarg = MiscPixels(s.height) - dw;
  1130.         else if (pLoc < pMax)
  1131.             xTarg = pMax - pLoc - dw;
  1132.         else
  1133.             xTarg = 0;
  1134.         }
  1135.         MiscRect_O rVis( isHorz );
  1136.         rVis.setX_O( xTarg );
  1137.         rVis.setWidth_O( rDrag.getWidth_O() );
  1138.         rVis.setHeight_O( rDrag.getHeight_O() );
  1139.         [visCache compositeToPoint:pt fromRect:rVis
  1140.             operation:NSCompositeCopy];
  1141.         }
  1142.  
  1143.     if (event == 0)
  1144.         break;
  1145.     else if ([event type] == NSLeftMouseUp)
  1146.         {
  1147.         mouseUpPt = [self convertPoint:[event locationInWindow] fromView:0];
  1148.         break;
  1149.         }
  1150.     else if ([event type] != NSPeriodic)
  1151.         {
  1152.         [lastEvent release];
  1153.         lastEvent = [event copy];
  1154.         }
  1155.  
  1156.     MiscPixels mLoc = MiscPixels( [self offsetFromEvent:lastEvent] );
  1157.     if ((mLoc < pMin && pMin > 0.0) || (mLoc > pMax &&
  1158.         pMax < MiscRect_O( isHorz, [self bounds] ).getMaxX_O()))
  1159.         {
  1160.         [self autoscroll:lastEvent];
  1161.         [visCache release];
  1162.         visCache = [[self getVisibleCacheMin:&pMin max:&pMax] retain];
  1163.         mLoc = MiscPixels( [self offsetFromEvent:lastEvent] );
  1164.         startTimer();
  1165.         }
  1166.     else
  1167.         {
  1168.         stopTimer();
  1169.         }
  1170.  
  1171.     pLoc = mLoc - delta;
  1172.     if (pLoc < pMin - size.getWidth_O())
  1173.         pLoc = pMin - size.getWidth_O();
  1174.     else if (pLoc > pMax)
  1175.         pLoc = pMax;
  1176.     }
  1177.  
  1178.     [lastEvent release];
  1179.     stopTimer();
  1180.     [scroll unlockFocus];
  1181.     [self clearWells:&w1 :&w2];
  1182.     [visCache release];
  1183.     [dragCache release];
  1184.  
  1185.     MiscCoord_V const toPos = [self calcDrop:pos :mouseDownPt :mouseUpPt];
  1186.  
  1187.     if (toPos != pos)
  1188.     {
  1189.     [scroll border:[self borderType] moveSlotFrom:pos to:toPos];
  1190.     [win invalidateCursorRectsForView:self];
  1191.     [scroll border:[self borderType] slotDraggedFrom:pos to:toPos];
  1192.     [self setNeedsDisplay:YES];
  1193.     [[scroll documentView] setNeedsDisplay:YES];
  1194.     }
  1195.     else
  1196.     {
  1197.     // Need to redisplay the slot that the wells were covering.
  1198.     MiscBorderType const b = [self borderType];
  1199.     int const phys_pos = [scroll border:b slotAtPosition:toPos];
  1200.     [scroll border:b drawSlotTitle:phys_pos];
  1201.     [scroll border:b drawSlot:phys_pos];
  1202.     }
  1203.  
  1204.     [scroll enableCursor];
  1205.     [scroll displayIfNeeded];
  1206.     [win enableFlushWindow];
  1207.     [win flushWindow];
  1208.     return toPos;
  1209.     }
  1210.  
  1211.  
  1212. //-----------------------------------------------------------------------------
  1213. // - awaitDragEvent:inPos:
  1214. //-----------------------------------------------------------------------------
  1215. - (MiscCoord_V)awaitDragEvent:(NSEvent*)event inPos:(MiscCoord_V)pos
  1216.     {
  1217.     MiscCoord_V toPos = pos;
  1218.     int const WANTED = (NSLeftMouseUpMask | NSLeftMouseDraggedMask);
  1219.     float const SLOP = 2.0;
  1220.     NSWindow* win = [self window];
  1221.     NSEvent* mouseDown = [event copy];
  1222.     NSPoint origLoc = [mouseDown locationInWindow];
  1223.     for (;;)
  1224.     {
  1225.     NSEvent* p = [win nextEventMatchingMask:WANTED
  1226.             untilDate:[NSDate distantFuture]
  1227.             inMode:NSEventTrackingRunLoopMode
  1228.             dequeue:NO];
  1229.     if (p == 0)
  1230.         break;
  1231.     else if ([p type] == NSLeftMouseUp)
  1232.         {
  1233.         [win nextEventMatchingMask:NSLeftMouseUpMask];
  1234.         break;
  1235.         }
  1236.     else // ([p type] == NSLeftMouseDragged)
  1237.         {
  1238.         NSPoint newLoc = [p locationInWindow];
  1239.         if (newLoc.x > origLoc.x + SLOP || newLoc.y > origLoc.y + SLOP ||
  1240.         newLoc.x < origLoc.x - SLOP || newLoc.y < origLoc.y - SLOP)
  1241.         {
  1242.         [scroll suspendEditing];
  1243.         toPos = [self dragEvent:mouseDown inPos:pos];
  1244.         [scroll resumeEditing];
  1245.         break;
  1246.         }
  1247.         else
  1248.         [win nextEventMatchingMask:NSLeftMouseDraggedMask];
  1249.         }
  1250.     }
  1251.     [mouseDown release];
  1252.     return toPos;
  1253.     }
  1254.     
  1255.  
  1256.  
  1257. //=============================================================================
  1258. // TOGGLE SORT DIRECTION
  1259. //=============================================================================
  1260. //-----------------------------------------------------------------------------
  1261. // - toggleRectForPos:
  1262. //-----------------------------------------------------------------------------
  1263. - (NSRect)toggleRectForPos:(MiscCoord_V)pos
  1264.     {
  1265.     NSRect r = [self rectForPos:pos];
  1266.     float const w = TOGGLE_WIDTH + MISC_RESIZE_EPSILON;
  1267.     r.origin.x = floor( r.origin.x + r.size.width - w );
  1268.     r.size.width = w;
  1269.     return r;
  1270.     }
  1271.  
  1272.  
  1273. //-----------------------------------------------------------------------------
  1274. // - inToggleZone:forPos:
  1275. //-----------------------------------------------------------------------------
  1276. - (BOOL)inToggleZone:(NSPoint)pt forPos:(MiscCoord_V)pos
  1277.     {
  1278.     if ([scroll autoSortSlots:MISC_OTHER_BORDER([self borderType])] &&
  1279.     info->isSortable(pos))
  1280.     {
  1281.     NSRect r = [self toggleRectForPos:pos];
  1282.     return r.origin.x <= pt.x && pt.x <= (r.origin.x + r.size.width);
  1283.     }
  1284.     return NO;
  1285.     }
  1286.  
  1287.  
  1288. //-----------------------------------------------------------------------------
  1289. // - toggleEvent:forPos:
  1290. //-----------------------------------------------------------------------------
  1291. - (void)toggleEvent:(NSEvent*)p forPos:(MiscCoord_V)pos
  1292.     {
  1293.     BOOL was_in_zone = YES;
  1294.     BOOL is_in_zone = YES;
  1295.  
  1296.     NSRect toggleZone = [self toggleRectForPos:pos];
  1297.     NSWindow* win = [self window];
  1298.  
  1299.     togglePos = pos;
  1300.     [self lockFocus];
  1301.     [self drawPos:pos];
  1302.     [win flushWindow];
  1303.  
  1304.     for (;;)
  1305.     {
  1306.     p = [win nextEventMatchingMask:
  1307.             (NSLeftMouseUpMask | NSLeftMouseDraggedMask)];
  1308.  
  1309.     NSPoint new_loc = [p locationInWindow];
  1310.     new_loc = [self convertPoint:new_loc fromView:0];
  1311.     is_in_zone = NSPointInRect( new_loc, toggleZone );
  1312.     if (was_in_zone != is_in_zone)
  1313.         {
  1314.         was_in_zone = is_in_zone;
  1315.         togglePos = (is_in_zone ? pos : -1);
  1316.         [self drawPos:pos];
  1317.         [win flushWindow];
  1318.         }
  1319.  
  1320.     if ([p type] == NSLeftMouseUp)
  1321.         break;
  1322.     }
  1323.  
  1324.     togglePos = -1;
  1325.     [self unlockFocus];
  1326.  
  1327.     if (is_in_zone)
  1328.     {
  1329.     MiscBorderType const bt = [self borderType];
  1330.     MiscSortDirection dir = info->getSortDirection( pos );
  1331.     dir = MISC_OTHER_DIRECTION( dir );
  1332.     MiscCoord_P p_pos = info->visualToPhysical( pos );
  1333.     [scroll border:bt setSlot:p_pos sortDirection:dir];
  1334.     [scroll border:bt slotSortReversed:p_pos];
  1335.     [self setNeedsDisplayInRect:[self rectForPos:pos]];
  1336.     [scroll displayIfNeeded];
  1337.     }
  1338.     }
  1339.  
  1340.  
  1341.  
  1342. //=============================================================================
  1343. // SELECTION
  1344. //=============================================================================
  1345. //-----------------------------------------------------------------------------
  1346. // - adjustCursor:
  1347. //-----------------------------------------------------------------------------
  1348. - (void)adjustCursor:(MiscCoord_V)p
  1349.     {
  1350.     if (p < 0)
  1351.     p = 0;
  1352.     else if (p >= info->count())
  1353.     p = info->count() - 1;
  1354.     [scroll border:[self borderType] setCursorSlot:info->visualToPhysical(p)];
  1355.     }
  1356.  
  1357.  
  1358. //-----------------------------------------------------------------------------
  1359. // - selectionEvent:fromPos:
  1360. //-----------------------------------------------------------------------------
  1361. - (void)selectionEvent:(NSEvent*)p fromPos:(MiscCoord_V)pos
  1362.     {
  1363.     unsigned int const WANTED =
  1364.         (NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSPeriodicMask);
  1365.     BOOL doubleClicked = ([p clickCount] > 1);
  1366.  
  1367.     [scroll disableCursor];
  1368.     [self clearOtherBorder];
  1369.     [tracker mouseDown:p atPos:pos];
  1370.     [scroll selectionChanged];
  1371.     [scroll displayIfNeeded];
  1372.  
  1373.     startTimer();
  1374.     NSEvent* lastEvent = [p copy];
  1375.  
  1376.     for(;;)
  1377.     {
  1378.     p = [[self window] nextEventMatchingMask:WANTED];
  1379.  
  1380.     if (p == 0 || [p type] == NSLeftMouseUp)
  1381.         break;
  1382.     else if ([p type] == NSPeriodic)
  1383.         [self autoscroll:lastEvent];
  1384.     else
  1385.         {
  1386.         [lastEvent release];
  1387.         lastEvent = [p copy];
  1388.         }
  1389.  
  1390.     NSPoint new_loc = [lastEvent locationInWindow];
  1391.     new_loc = [self convertPoint:new_loc fromView:0];
  1392.     MiscCoord_V new_pos = [self posForMousePt:new_loc];
  1393.     if (new_pos != pos)
  1394.         {
  1395.         pos = new_pos;
  1396.         [tracker mouseDragged:p atPos:pos];
  1397.         [scroll selectionChanged];
  1398.         [scroll displayIfNeeded];
  1399.         }
  1400.     }
  1401.  
  1402.     [lastEvent release];
  1403.     stopTimer();
  1404.  
  1405.     [tracker mouseUp:p atPos:pos];
  1406.     [scroll selectionChanged];
  1407.     [self adjustCursor:pos];
  1408.     [scroll enableCursor];
  1409.     [[self window] flushWindow];
  1410.  
  1411.     [scroll displayIfNeeded];
  1412.     if ([scroll isEnabled])
  1413.     {
  1414.     [scroll sendAction];
  1415.     if (doubleClicked)
  1416.         [scroll sendDoubleAction];
  1417.     }
  1418.     }
  1419.  
  1420.  
  1421.  
  1422. //=============================================================================
  1423. // MOUSE-EVENTS
  1424. //=============================================================================
  1425. //-----------------------------------------------------------------------------
  1426. // - acceptsFirstMouse:
  1427. //-----------------------------------------------------------------------------
  1428. - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent
  1429.     {
  1430.     return YES;
  1431.     }
  1432.  
  1433.  
  1434. //-----------------------------------------------------------------------------
  1435. // - mouseDown:
  1436. //-----------------------------------------------------------------------------
  1437. - (void)mouseDown:(NSEvent*)p 
  1438.     {
  1439.     NSPoint evpt = [self convertPoint:[p locationInWindow] fromView:0];
  1440.     MiscCoord_V pos = [self posForMousePt:evpt];
  1441.     MiscPixels x, delta_x;
  1442.  
  1443.     if ([self inResizeZone:evpt forPos:&pos atX:&x deltaX:&delta_x])
  1444.     {
  1445.     [scroll suspendEditing];
  1446.     MiscPixels finalWidth;
  1447.     BOOL doit;
  1448.     doit = [self resizeEvent:p inPos:pos atX:x deltaX:delta_x
  1449.             finalWidth:&finalWidth];
  1450.     if (doit)
  1451.         {
  1452.         if (info->isUniformSize())
  1453.         {
  1454.         [scroll border:[self borderType]
  1455.             setUniformSizeSlots:finalWidth];
  1456.         [[self window] invalidateCursorRectsForView:self];
  1457.         [scroll setNeedsDisplay:YES];
  1458.         }
  1459.         else
  1460.         [self setPos:pos width:finalWidth];
  1461.         [scroll border:[self borderType] slotResized:pos];
  1462.         }
  1463.     [scroll resumeEditing];
  1464.     }
  1465.     else if ([self inToggleZone:evpt forPos:pos])
  1466.     {
  1467.     [scroll suspendEditing];
  1468.     [self toggleEvent:p forPos:pos];
  1469.     [scroll resumeEditing];
  1470.     }
  1471.     else if (info->isDraggable() && (info->isModifierDrag() ==
  1472.     (([p modifierFlags] & NSCommandKeyMask) != 0)))
  1473.     {
  1474.     [self awaitDragEvent:p inPos:pos];
  1475.     }
  1476.     else
  1477.     {
  1478.     NSParameterAssert( tracker != 0 );    // End cell editing.
  1479.     [[self window] makeFirstResponder:[scroll documentView]];
  1480.     [self selectionEvent:p fromPos:pos];
  1481.     }
  1482.     }
  1483.  
  1484. @end
  1485.