home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / YellowBox / Kits / MiscTableScroll-138.1 / Palettes / MiscTableScroll / Framework / MiscTableBorder.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-31  |  33.1 KB  |  1,274 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. // MiscTableBorder.cc
  17. //
  18. //    Structure describing border of an MiscTableView.
  19. //
  20. //    NOTE: many of the sub-arrays in an MiscTableBorder are conditional.
  21. //    They are not guaranteed to be allocated for every instance.  They are
  22. //    only allocated when the caller tries to store a value in them.
  23. //
  24. //    FIXME: Optimization: separate slot-offset calculations from
  25. //    resizing calculations.    Many situations only require an offset-
  26. //    update, not a full size recalc.
  27. //
  28. //-----------------------------------------------------------------------------
  29. //-----------------------------------------------------------------------------
  30. // $Id: MiscTableBorder.cc,v 1.29 98/03/29 23:47:03 sunshine Exp $
  31. // $Log:    MiscTableBorder.cc,v $
  32. //  Revision 1.29  98/03/29  23:47:03  sunshine
  33. //  v138.1: #import was missing "MiscTableScroll/" for public headers.
  34. //  Now uses MISC_FRAME_HEIGHT rather than hard-coded "18".
  35. //  
  36. //  Revision 1.28  98/03/23  21:39:45  sunshine
  37. //  v137.1: Added clearSortDirection().
  38. //  
  39. //  Revision 1.27  98/03/23  08:42:26  sunshine
  40. //  v135.1: Dropped clearVMap().  Added swapSlots().
  41. //-----------------------------------------------------------------------------
  42. #ifdef __GNUC__
  43. # pragma implementation
  44. #endif
  45. #include "MiscTableBorder.h"
  46. #include "MiscTableScrollPrivate.h"
  47. #include "MiscTableUtil.h"
  48. #include <MiscTableScroll/MiscTableScroll.h>
  49. #include <MiscTableScroll/MiscTableCell.h>
  50. extern "Objective-C" {
  51. #import <AppKit/NSCell.h>
  52. #import <AppKit/NSButtonCell.h>
  53. #import <AppKit/NSImage.h>
  54. }
  55. extern "C" {
  56. #include <math.h>    // floor()
  57. #include <stdlib.h>
  58. #include <string.h>
  59. }
  60.  
  61. #define MISC_SWAP(T,V,X,Y)\
  62.     if ((V) != 0) { T const t = (V)[X]; (V)[X] = (V)[Y]; (V)[Y] = t; }
  63.  
  64. #define MISC_SIZE_CHECK(X)\
  65.     do {\
  66.     NSCParameterAssert( 0 <= X );\
  67.     NSCParameterAssert( X <= MISC_MAX_PIXELS_SIZE );\
  68.     } while(0)
  69.  
  70. #define MISC_RANGE_CHECK(X)\
  71.     NSCParameterAssert( 0 <= X );  NSCParameterAssert( X < num_slots )
  72. #define MISC_RANGE_CHECK_1(X)\
  73.     NSCParameterAssert( 0 <= X );  NSCParameterAssert( X <= num_slots )
  74.  
  75. #define MISC_SLOT_MEMBER(X,M)    ((slots != 0) ? slots[X].M : def_slot.M)
  76. #define MISC_SET_SLOT_MEMBER(X,N,M)\
  77.     do {\
  78.     needs_recalc = true;\
  79.     if (slots == 0) alloc_slots();\
  80.     slots[X].M = N;\
  81.     } while (0)
  82.  
  83. #define MISC_MAP(M,I)    (((M) != 0) ? (M)[I] : I)
  84.  
  85. #define    MISC_DEF_SORT_DIR    MISC_SORT_ASCENDING
  86. #define    MISC_DEF_SORT_TYPE    MISC_SORT_STRING_CASE_INSENSITIVE
  87. #define MISC_DEF_SORT_INFO    ((MISC_DEF_SORT_TYPE << 1) | \
  88.                     (MISC_DEF_SORT_DIR & 1))
  89.  
  90. //-----------------------------------------------------------------------------
  91. // global_grow
  92. //-----------------------------------------------------------------------------
  93. void MiscTableBorder::global_grow( MiscPixels total_size )
  94.     {
  95.     MiscPixels offset = 0;
  96.     MiscPixels deficit = min_total_size - total_size;
  97.     MiscPixels const adj = deficit / num_springy;
  98.     int nextra = (int) (deficit - adj * num_springy);
  99.     for (int i = 0; i < num_slots; i++)
  100.     {
  101.     MiscTableSlot& r = slots[i];
  102.     if (r.isSpringy())
  103.         {
  104.         MiscPixels delta = adj;
  105.         if (nextra > 0)
  106.         {
  107.         nextra--;
  108.         delta++;
  109.         }
  110.         r.adj_size = r.size + delta;
  111.         }
  112.     r.offset = offset;
  113.     offset += r.adj_size;
  114.     }
  115.     }
  116.  
  117.  
  118. //-----------------------------------------------------------------------------
  119. // perform_recalc
  120. //-----------------------------------------------------------------------------
  121. void MiscTableBorder::perform_recalc()
  122.     {
  123.     MISC_SIZE_CHECK( min_total_size );
  124.  
  125.     int i;
  126.     MiscPixels total_size = 0;
  127.  
  128.     if (slots == 0)
  129.     alloc_slots();
  130.  
  131.     for (i = 0;     i < num_slots;     i++)
  132.     {
  133.     MiscTableSlot& r = slots[i];
  134.     NSCParameterAssert( 0 <= r.min_size );
  135.     NSCParameterAssert( r.min_size <= r.size );
  136.     NSCParameterAssert( r.size <= r.max_size );
  137.     NSCParameterAssert( r.max_size <= MISC_MAX_PIXELS_SIZE );
  138.     r.offset = total_size;
  139.     r.adj_size = r.size;
  140.     total_size += r.adj_size;
  141.     }
  142.  
  143.     if (total_size < min_total_size && num_springy > 0)
  144.     global_grow( total_size );
  145.     }
  146.  
  147.  
  148. //-----------------------------------------------------------------------------
  149. // do_recalc
  150. //-----------------------------------------------------------------------------
  151. void MiscTableBorder::do_recalc()
  152.     {
  153.     needs_recalc = false;
  154.     if (uniform_size == 0 && num_slots > 0)
  155.     perform_recalc();
  156.     }
  157.  
  158.  
  159. //-----------------------------------------------------------------------------
  160. // recalc_if_needed
  161. //-----------------------------------------------------------------------------
  162. inline void MiscTableBorder::recalc_if_needed()
  163.     {
  164.     if (needs_recalc)
  165.     do_recalc();
  166.     }
  167.  
  168.  
  169. //-----------------------------------------------------------------------------
  170. // recalcOffsets
  171. //-----------------------------------------------------------------------------
  172. void MiscTableBorder::recalcOffsets()
  173.     {
  174.     needs_recalc = true;
  175.     recalc_if_needed();
  176.     }
  177.  
  178.  
  179. //-----------------------------------------------------------------------------
  180. // alloc_size
  181. //-----------------------------------------------------------------------------
  182. inline int MiscTableBorder::alloc_size( int rec_size )
  183.     { return rec_size * max_slots; }
  184.  
  185.  
  186. //-----------------------------------------------------------------------------
  187. // do_alloc
  188. //-----------------------------------------------------------------------------
  189. void* MiscTableBorder::do_alloc( int size )
  190.     { return malloc( alloc_size(size) ); }
  191.  
  192.  
  193. //-----------------------------------------------------------------------------
  194. // do_alloc_init
  195. //-----------------------------------------------------------------------------
  196. void* MiscTableBorder::do_alloc_init( int size )
  197.     {
  198.     int const nbytes = alloc_size( size );
  199.     void* const p = malloc( nbytes );
  200.     memset( p, 0, nbytes );
  201.     return p;
  202.     }
  203.  
  204.  
  205. //-----------------------------------------------------------------------------
  206. // do_realloc
  207. //-----------------------------------------------------------------------------
  208. void* MiscTableBorder::do_realloc( void* p, int size )
  209.     {
  210.     if (p != 0)
  211.     {
  212.     if (max_slots != 0)
  213.         {
  214.         p = realloc( p, max_slots * size );
  215.         }
  216.     else
  217.         {
  218.         free( p );
  219.         p = 0;
  220.         }
  221.     }
  222.     return p;
  223.     }
  224.  
  225.  
  226. //-----------------------------------------------------------------------------
  227. // do_realloc
  228. //-----------------------------------------------------------------------------
  229. void MiscTableBorder::do_realloc()
  230.     {
  231.     slots  = (MiscTableSlot*) do_realloc( slots, sizeof(*slots) );
  232.     v2p       = (MiscCoord_P*) do_realloc( v2p, sizeof(*v2p) );
  233.     p2v       = (MiscCoord_V*) do_realloc( p2v, sizeof(*p2v) );
  234.     tags   = (int*) do_realloc( tags, sizeof(*tags) );
  235.     titles = (NSString**) do_realloc( titles, sizeof(*titles) );
  236.     styles = (MiscTableCellStyle*) do_realloc( styles, sizeof(*styles) );
  237.     prototypes = (id*) do_realloc( prototypes, sizeof(*prototypes) );
  238.     sort_funcs = (MiscCompareEntryFunc*)
  239.             do_realloc( sort_funcs, sizeof(*sort_funcs) );
  240.     sort_info = (int*) do_realloc(sort_info,sizeof(*sort_info));
  241.     }
  242.  
  243.  
  244. //-----------------------------------------------------------------------------
  245. // freeExtraCapacity
  246. //-----------------------------------------------------------------------------
  247. void MiscTableBorder::freeExtraCapacity()
  248.     {
  249.     if (max_slots > num_slots)
  250.     {
  251.     max_slots = num_slots;
  252.     do_realloc();
  253.     }
  254.     }
  255.  
  256.  
  257. //-----------------------------------------------------------------------------
  258. // setCapacity
  259. //-----------------------------------------------------------------------------
  260. void MiscTableBorder::setCapacity( int x )
  261.     {
  262.     if (max_slots < x)
  263.     {
  264.     max_slots = x;
  265.     do_realloc();
  266.     }
  267.     }
  268.  
  269.  
  270. //-----------------------------------------------------------------------------
  271. // empty
  272. //-----------------------------------------------------------------------------
  273. void MiscTableBorder::empty()
  274.     {
  275.     setCount(0);
  276.     selectNone();
  277.     clearCursor();
  278.     clearClickedSlot();
  279.     }
  280.  
  281.  
  282. //-----------------------------------------------------------------------------
  283. // DELETE
  284. //-----------------------------------------------------------------------------
  285. void MiscTableBorder::destroy_slot( MiscCoord_V x, MiscCoord_P p )
  286.     {
  287.     if (isSpringy(x)) num_springy--;
  288.     if (titles != 0 && titles[p] != 0)        [titles[p] release];
  289.     if (prototypes != 0 && prototypes[p] != 0)    [prototypes[p] release];
  290.     }
  291.  
  292.  
  293. void MiscTableBorder::do_delete( void* p, int n, int i )
  294.     {
  295.     if (p != 0)
  296.     memmove( p + i * n, p + (i + 1) * n, (num_slots - i) * n );
  297.     }
  298.  
  299.  
  300. void MiscTableBorder::do_delete( MiscCoord_V x, MiscCoord_P p )
  301.     {
  302.     if (x < num_slots)
  303.     {
  304.     do_delete( slots, sizeof(*slots), x );
  305.     do_delete( v2p, sizeof(*v2p), x );
  306.     }
  307.     if (p < num_slots)
  308.     {
  309.     do_delete( tags, sizeof(*tags), p );
  310.     do_delete( titles, sizeof(*titles), p );
  311.     do_delete( styles, sizeof(*styles), p );
  312.     do_delete( prototypes, sizeof(*prototypes), p );
  313.     do_delete( sort_funcs, sizeof(*sort_funcs), p );
  314.     do_delete( sort_info, sizeof(*sort_info), p );
  315.     do_delete( p2v, sizeof(*p2v), p );
  316.     }
  317.     }
  318.  
  319.  
  320. void MiscTableBorder::deleteAt( MiscCoord_V x )
  321.     {
  322.     MISC_RANGE_CHECK( x );
  323.  
  324.     selection.shiftDownAt( x );
  325.     if (num_slots <= 1 || num_slots <= getCursor() + 1)
  326.     clearCursor();
  327.     if (num_slots <= 1 || num_slots <= clickedSlot() + 1)
  328.     clearClickedSlot();
  329.  
  330.     needs_recalc = true;
  331.  
  332.     MiscCoord_P const p = visualToPhysical(x);
  333.  
  334.     destroy_slot( x, p );
  335.  
  336.     num_slots--;
  337.     do_delete( x, p );
  338.  
  339.     if (v2p != 0)
  340.     {
  341.     for (int i = 0;     i < num_slots;     i++)
  342.         {
  343.         if (v2p[i] >= p)
  344.         v2p[i]--;
  345.         if (p2v[i] >= x)
  346.         p2v[i]--;
  347.         }
  348.     }
  349.     fixSelectedSlot();
  350.     }
  351.  
  352.  
  353. //-----------------------------------------------------------------------------
  354. // INSERT
  355. //-----------------------------------------------------------------------------
  356. void MiscTableBorder::init_slot( MiscCoord_V x, MiscCoord_P p )
  357.     {
  358.     if (def_slot.isSpringy())
  359.     num_springy++;
  360.  
  361.     if (slots != 0)        slots[x] = def_slot;
  362.     if (tags != 0)        tags[p] = def_tag;
  363.     if (titles != 0)        titles[p] = 0;
  364.     if (styles != 0)        styles[p] = def_style;
  365.     if (prototypes != 0)    prototypes[p] = 0;
  366.     if (sort_funcs != 0)    sort_funcs[p] = 0;
  367.     if (sort_info != 0)        sort_info[p] = MISC_DEF_SORT_INFO;
  368.  
  369.     if (v2p != 0)
  370.     {
  371.     v2p[x] = p;
  372.     p2v[p] = x;
  373.     }
  374.     }
  375.  
  376.  
  377. void MiscTableBorder::do_insert( void* p, int n, int i )
  378.     {
  379.     if (p != 0)
  380.     memmove( p + (i + 1) * n, p + i * n, (num_slots - i) * n );
  381.     }
  382.  
  383.  
  384. void MiscTableBorder::do_insert( MiscCoord_V x, MiscCoord_P p )
  385.     {
  386.     if (x < num_slots)
  387.     {
  388.     do_insert( slots, sizeof(*slots), x );
  389.     do_insert( v2p, sizeof(*v2p), x );
  390.     }
  391.     if (p < num_slots)
  392.     {
  393.     do_insert( titles, sizeof(*titles), p );
  394.     do_insert( tags, sizeof(*tags), p );
  395.     do_insert( styles, sizeof(*styles), p );
  396.     do_insert( prototypes, sizeof(*prototypes), p );
  397.     do_insert( sort_funcs, sizeof(*sort_funcs), p );
  398.     do_insert( sort_info, sizeof(*sort_info), p );
  399.     do_insert( p2v, sizeof(*p2v), p );
  400.     }
  401.     }
  402.  
  403.  
  404. void MiscTableBorder::insertAt( MiscCoord_V x, MiscCoord_P p )
  405.     {
  406.     MISC_RANGE_CHECK_1( x );
  407.  
  408.     needs_recalc = true;
  409.  
  410.     if (num_slots >= max_slots)
  411.     setCapacity( max_slots + 1 );
  412.  
  413.     do_insert( x, p );
  414.  
  415.     if (v2p != 0)
  416.     {
  417.     for (int i = 0;     i < num_slots;     i++)
  418.         {
  419.         if (p2v[i] >= x)
  420.         p2v[i]++;
  421.         if (v2p[i] >= p)
  422.         v2p[i]++;
  423.         }
  424.     }
  425.  
  426.     init_slot( x, p );
  427.  
  428.     num_slots++;
  429.  
  430.     selection.shiftUpAt( x );
  431.     if (selected_slot >= x)
  432.     selected_slot++;
  433.     if (hasValidCursor() && cursor >= x)
  434.     cursor++;
  435.     if (clicked_slot >= x)
  436.     clicked_slot++;
  437.     }
  438.  
  439.  
  440. //-----------------------------------------------------------------------------
  441. // do_shift
  442. //-----------------------------------------------------------------------------
  443. void MiscTableBorder::do_shift( void* p, int n, int i, int j )
  444.     {
  445.     if (p != 0)
  446.     if (i < j)
  447.         memmove( p + i * n, p + (i + 1) * n, (j - i) * n );
  448.     else
  449.         memmove( p + (j  + 1) * n, p + j * n, (i - j) * n );
  450.     }
  451.  
  452.  
  453. //-----------------------------------------------------------------------------
  454. // do_shift
  455. //-----------------------------------------------------------------------------
  456. void MiscTableBorder::do_shift( MiscCoord_V from, MiscCoord_V to )
  457.     {
  458.     do_shift( slots, sizeof(*slots), from, to );
  459.     do_shift( v2p, sizeof(*v2p), from, to );
  460.     }
  461.  
  462.  
  463. //-----------------------------------------------------------------------------
  464. // alloc_vmap
  465. //-----------------------------------------------------------------------------
  466. void MiscTableBorder::alloc_vmap()
  467.     {
  468.     p2v = (MiscCoord_V*) do_alloc( sizeof(*p2v) );
  469.     v2p = (MiscCoord_P*) do_alloc( sizeof(*v2p) );
  470.     for (int i = 0;  i < num_slots;  i++)
  471.     { p2v[i] = i; v2p[i] = i; }
  472.     }
  473.  
  474.  
  475. //-----------------------------------------------------------------------------
  476. // moveFromTo
  477. //-----------------------------------------------------------------------------
  478. void MiscTableBorder::moveFromTo( MiscCoord_V from, MiscCoord_V to )
  479.     {
  480.     MISC_RANGE_CHECK( from ); MISC_RANGE_CHECK( to );
  481.  
  482.     bool const was_cursor     = (cursor == from);
  483.     bool const was_clicked_slot  = (clicked_slot == from);
  484.     bool const was_selected_slot = (selected_slot == from);
  485.     bool const was_selected     = selection.contains( from );
  486.     selection.shiftDownAt( from );
  487.  
  488.     needs_recalc = true;
  489.  
  490.     MiscCoord_P const p = visualToPhysical( from );
  491.     MiscTableSlot const tmp_slot = slots ? slots[from] : def_slot;
  492.  
  493.     if (p2v == 0)
  494.     alloc_vmap();
  495.  
  496.     do_shift( from, to );
  497.  
  498.     if (from < to)
  499.     {
  500.     for (int i = 0;     i < num_slots;     i++)
  501.         if (from < p2v[i] && p2v[i] <= to)
  502.         p2v[i]--;
  503.     if (!was_cursor && from <= cursor && cursor >= to)
  504.         cursor--;
  505.     }
  506.     else
  507.     {
  508.     for (int i = 0;     i < num_slots;     i++)
  509.         if (to <= p2v[i] && p2v[i] < from)
  510.         p2v[i]++;
  511.     if (!was_cursor && from <= cursor && cursor >= to)
  512.         cursor++;
  513.     }
  514.     p2v[p] = to;
  515.     v2p[to] = p;
  516.  
  517.     if (slots != 0) slots[to]  = tmp_slot;
  518.  
  519.     selection.shiftUpAt( to );
  520.     if (was_selected)        selection.add( to );
  521.     if (was_selected_slot)    setSelectedSlot( to );
  522.     if (was_clicked_slot)    setClickedSlot( to );
  523.     if (was_cursor)        cursor = to;
  524.     }
  525.  
  526.  
  527. //-----------------------------------------------------------------------------
  528. // setCount
  529. //-----------------------------------------------------------------------------
  530. void MiscTableBorder::setCount( int x )
  531.     {
  532.     setP2VMap(0);
  533.     if (num_slots != x)
  534.     {
  535.     needs_recalc = true;
  536.     int const old_slots = num_slots;
  537.     num_slots = x;
  538.     setCapacity( x );    // Only increases capacity, never decreases.
  539.  
  540.     if (old_slots < num_slots)
  541.         {
  542.         for (int i = old_slots;  i < num_slots;  i++)
  543.         init_slot( i, i );
  544.         }
  545.     else
  546.         {
  547.         NSCParameterAssert( old_slots <= max_slots );
  548.         num_slots = old_slots;
  549.         for (int i = x;  i < old_slots;  i++)
  550.         destroy_slot( i, visualToPhysical(i) );
  551.         num_slots = x;
  552.  
  553.         selection.remove( num_slots, old_slots );
  554.         if (selectedSlot() >= num_slots)    fixSelectedSlot();
  555.         if (cursor >= num_slots)        clearCursor();
  556.         if (clicked_slot >= num_slots)    clearClickedSlot();
  557.         }
  558.     }
  559.     }
  560.  
  561.  
  562. //-----------------------------------------------------------------------------
  563. // good_map
  564. //    'map' is a good map if all the values are in-range, and no value
  565. //    is repeated.  A null map represents a normal sequential series.
  566. //    'onescomp' interprets negative values as the one's complement of a
  567. //    slot number.
  568. //-----------------------------------------------------------------------------
  569. bool MiscTableBorder::good_map( int const* map, bool onescomp ) const
  570.     {
  571.     bool answer = true;
  572.     int const lim = count();
  573.     if (lim > 0)
  574.     {
  575.     if (map != 0)
  576.         {
  577.         bool* seen = (bool*) calloc( sizeof(bool), lim );
  578.         for (int i = 0;  i < lim;  i++)
  579.         {
  580.         int x = map[i];
  581.         if (onescomp && x < 0)
  582.             x = ~x;
  583.         if ((unsigned int) x < (unsigned int)lim && !seen[i])
  584.             seen[i] = true;
  585.         else
  586.             {
  587.             answer = false;
  588.             break;
  589.             }
  590.         }
  591.         free( seen );
  592.         }
  593.     }
  594.     return answer;
  595.     }
  596.  
  597.  
  598. //-----------------------------------------------------------------------------
  599. // do_remap
  600. //-----------------------------------------------------------------------------
  601. void* MiscTableBorder::do_remap( void* p, int n, MiscCoord_V const* new_p2v )
  602.     {
  603.     if (p != 0)
  604.     {
  605.     void* t = do_alloc( n );
  606.     for (int i = 0;     i < num_slots;     i++)
  607.         memcpy( t + MISC_MAP(new_p2v,i) * n,
  608.             p + MISC_MAP(p2v,i) * n, n );
  609.     free( p );
  610.     p = t;
  611.     }
  612.     return p;
  613.     }
  614.  
  615.  
  616. //-----------------------------------------------------------------------------
  617. // do_remap
  618. //-----------------------------------------------------------------------------
  619. void MiscTableBorder::do_remap( MiscCoord_V const* new_p2v )
  620.     {
  621.     if (num_slots > 0)
  622.     slots = (MiscTableSlot*) do_remap( slots, sizeof(*slots), new_p2v );
  623.     }
  624.  
  625.  
  626. //-----------------------------------------------------------------------------
  627. // setP2VMap
  628. //-----------------------------------------------------------------------------
  629. bool MiscTableBorder::setP2VMap( MiscCoord_V const* new_p2v )
  630.     {
  631.     if (good_map( (int const*) new_p2v, false ))
  632.     {
  633.     if (new_p2v != 0 || p2v != 0)
  634.         {
  635.         MiscSparseSet p_sel;
  636.         visualToPhysical( selection, p_sel );
  637.         MiscCoord_P const p_selSlot =
  638.             (selected_slot >= 0 ? 
  639.             visualToPhysical(selected_slot) : -1);
  640.  
  641.         needs_recalc = true;
  642.         do_remap( new_p2v );
  643.         if (p2v == 0)
  644.         {
  645.         p2v = (MiscCoord_V*) do_alloc( sizeof(*p2v) );
  646.         v2p = (MiscCoord_P*) do_alloc( sizeof(*v2p) );
  647.         }
  648.         if (new_p2v != 0 && p2v != 0)
  649.         {
  650.         for (int i = 0;  i < num_slots;  i++)
  651.             v2p[new_p2v[i]] = i;
  652.         memcpy( p2v, new_p2v, num_slots * sizeof(*p2v) );
  653.         }
  654.         else if (new_p2v == 0)
  655.         {
  656.         free( p2v );  p2v = 0;
  657.         free( v2p );  v2p = 0;
  658.         }
  659.  
  660.         physicalToVisual( p_sel, selection );
  661.         selected_slot = (p_selSlot >= 0 ? physicalToVisual(p_selSlot) : -1);
  662.         }
  663.     return true;
  664.     }
  665.     return false;
  666.     }
  667.  
  668.  
  669. //-----------------------------------------------------------------------------
  670. // setV2PMap
  671. //-----------------------------------------------------------------------------
  672. bool MiscTableBorder::setV2PMap( MiscCoord_P const* new_v2p )
  673.     {
  674.     if (good_map( (int const*) new_v2p, false ))
  675.     {
  676.     if (new_v2p != 0 || v2p != 0)
  677.         {
  678.         if (new_v2p != 0)
  679.         {
  680.         MiscCoord_V* new_p2v =
  681.             (MiscCoord_V*) malloc( sizeof(*new_p2v) * num_slots );
  682.         for (int i = 0;  i < num_slots;  i++)
  683.             new_p2v[new_v2p[i]] = i;
  684.         setP2VMap( new_p2v );
  685.         free( new_p2v );
  686.         }
  687.         else
  688.         setP2VMap( 0 );
  689.         }
  690.     return true;
  691.     }
  692.     return false;
  693.     }
  694.  
  695.  
  696. //-----------------------------------------------------------------------------
  697. // swapSlots
  698. //-----------------------------------------------------------------------------
  699. void MiscTableBorder::swapSlots( MiscCoord_P x, MiscCoord_P y )
  700.     {
  701.     MISC_SWAP( MiscTableSlot, slots, x, y )
  702.     MISC_SWAP( int, tags, x, y )
  703.     MISC_SWAP( NSString*, titles, x, y )
  704.     MISC_SWAP( MiscTableCellStyle, styles, x, y )
  705.     MISC_SWAP( id, prototypes, x, y )
  706.     MISC_SWAP( MiscCompareEntryFunc, sort_funcs, x, y )
  707.     MISC_SWAP( int, sort_info, x, y )
  708.     do_recalc();    // offsets only.
  709.     }
  710.  
  711.  
  712. //-----------------------------------------------------------------------------
  713. // setUniformSize
  714. //-----------------------------------------------------------------------------
  715. bool MiscTableBorder::setUniformSize( MiscPixels x )
  716.     {
  717.     bool const changed = (uniform_size != x);
  718.     if (changed)
  719.     {
  720.     needs_recalc = true;
  721.     if (uniform_size == 0)    // New, non-zero uniform size.
  722.         {
  723.         if (slots != 0)
  724.         { free( slots );  slots = 0; }
  725.         num_springy = 0;
  726.         min_total_size = 0;
  727.         }
  728.     uniform_size = x;
  729.     }
  730.     return changed;
  731.     }
  732.  
  733.  
  734. //-----------------------------------------------------------------------------
  735. // TOTAL SIZE
  736. //-----------------------------------------------------------------------------
  737. MiscPixels MiscTableBorder::totalSize()
  738.     {
  739.     if (num_slots == 0)
  740.     return 0;
  741.     else if (uniform_size != 0)
  742.     return (uniform_size * num_slots);
  743.     else
  744.     {
  745.     recalc_if_needed();
  746.     return getOffset( num_slots - 1 ) + effectiveSize( num_slots - 1 );
  747.     }
  748.     }
  749.  
  750. void MiscTableBorder::setMinTotalSize( MiscPixels x )
  751.     {
  752.     if (min_total_size != x)
  753.     {
  754.     min_total_size = x;
  755.     MISC_SIZE_CHECK( min_total_size );
  756.     needs_recalc = true;
  757.     }
  758.     }
  759.  
  760. //-----------------------------------------------------------------------------
  761. // EFFECTIVE SIZE
  762. //-----------------------------------------------------------------------------
  763. MiscPixels MiscTableBorder::effectiveSize( MiscCoord_V x )
  764.     {
  765.     MISC_RANGE_CHECK( x );
  766.     if (uniform_size != 0)
  767.     return uniform_size;
  768.     recalc_if_needed();
  769.     return MISC_SLOT_MEMBER( x, adj_size );
  770.     }
  771.  
  772.  
  773. MiscPixels MiscTableBorder::effectiveMinSize( MiscCoord_V x )
  774.     {
  775.     MISC_RANGE_CHECK( x );
  776.     if (uniform_size != 0)
  777.     return min_uniform_size;
  778.  
  779.     recalc_if_needed();
  780.  
  781.     MiscTableSlot const& r = slots[x];
  782.     MiscPixels slot_min = r.min_size;
  783.  
  784.     MiscPixels global_min = min_total_size;
  785.  
  786.     for (int i = 0;  i < num_slots;  i++)
  787.     {
  788.     if (i != x)
  789.         {
  790.         MiscTableSlot& t = slots[i];
  791.         if (t.isSpringy())
  792.         global_min -= t.max_size;
  793.         else
  794.         global_min -= t.size;
  795.         }
  796.     }
  797.  
  798.     if (slot_min < global_min)
  799.     slot_min = global_min;
  800.  
  801.     if (slot_min > r.adj_size)
  802.     slot_min = r.adj_size;
  803.  
  804.     return slot_min;
  805.     }
  806.  
  807.  
  808. //-----------------------------------------------------------------------------
  809. // alloc_slots
  810. //-----------------------------------------------------------------------------
  811. void MiscTableBorder::alloc_slots()
  812.     {
  813.     NSCParameterAssert( slots == 0 );
  814.     slots = (MiscTableSlot*) do_alloc( sizeof(*slots) );
  815.     for (int i = 0;  i < num_slots;  i++)
  816.     slots[i] = def_slot;
  817.     }
  818.  
  819.  
  820. //-----------------------------------------------------------------------------
  821. // OFFSETS
  822. //-----------------------------------------------------------------------------
  823. MiscCoord_V MiscTableBorder::find_slot_for_offset( MiscPixels x )
  824.     {
  825.     NSCParameterAssert( num_slots > 0 );
  826.     int lo = 0;
  827.     int hi = num_slots - 1;
  828.     while (lo <= hi)
  829.     {
  830.     int const mid = (lo + hi) >> 1;
  831.     if (slots[mid].offset <= x)
  832.         lo = mid + 1;
  833.     else
  834.         hi = mid - 1;
  835.     }
  836.     if (lo > 0 && (lo >= num_slots || slots[lo].offset > x))
  837.     lo--;
  838.     return lo;
  839.     }
  840.  
  841.  
  842. MiscCoord_V MiscTableBorder::visualForOffset( MiscPixels x )
  843.     {
  844.     MiscCoord_V i = -1;
  845.     if (x >= 0 && num_slots > 0)
  846.     {
  847.     if (uniform_size != 0)
  848.         {
  849.         i = (MiscCoord_V) (x / uniform_size);
  850.         }
  851.     else
  852.         {
  853.         recalc_if_needed();
  854.         i = find_slot_for_offset( x );
  855.         }
  856.     }
  857.     if (i >= num_slots)
  858.     i = num_slots - 1;
  859.     return i;
  860.     }
  861.  
  862.  
  863. MiscPixels MiscTableBorder::getOffset( MiscCoord_V x )
  864.     {
  865.     if (num_slots == 0)
  866.     return 0;
  867.     MISC_RANGE_CHECK( x );
  868.     if (uniform_size != 0)
  869.     return uniform_size * x;
  870.     recalc_if_needed();
  871.     return MISC_SLOT_MEMBER( x, offset );
  872.     }
  873.  
  874.  
  875. //-----------------------------------------------------------------------------
  876. // SIZE
  877. //-----------------------------------------------------------------------------
  878. MiscPixels MiscTableBorder::getSize( MiscCoord_V x ) const
  879.     {
  880.     if (num_slots == 0)
  881.     return 0;
  882.     MISC_RANGE_CHECK( x );
  883.     if (uniform_size != 0)
  884.     return uniform_size;
  885.     return MISC_SLOT_MEMBER( x, size );
  886.     }
  887.  
  888. void MiscTableBorder::setSize( MiscCoord_V x, MiscPixels n )
  889.     {
  890.     MISC_RANGE_CHECK( x );
  891.     if (uniform_size == 0)
  892.     MISC_SET_SLOT_MEMBER( x, n, size );
  893.     }
  894.  
  895.  
  896. MiscPixels MiscTableBorder::getMinSize( MiscCoord_V x ) const
  897.     {
  898.     MISC_RANGE_CHECK( x );
  899.     if (uniform_size != 0)
  900.     return uniform_size;
  901.     return MISC_SLOT_MEMBER( x, min_size );
  902.     }
  903.  
  904. void MiscTableBorder::setMinSize( MiscCoord_V x, MiscPixels n )
  905.     {
  906.     MISC_RANGE_CHECK( x );
  907.     if (uniform_size == 0)
  908.     MISC_SET_SLOT_MEMBER( x, n, min_size );
  909.     }
  910.  
  911.  
  912. MiscPixels MiscTableBorder::getMaxSize( MiscCoord_V x ) const
  913.     {
  914.     MISC_RANGE_CHECK( x );
  915.     if (uniform_size != 0)
  916.     return max_uniform_size;
  917.     return MISC_SLOT_MEMBER( x, max_size );
  918.     }
  919.  
  920. void MiscTableBorder::setMaxSize( MiscCoord_V x, MiscPixels n )
  921.     {
  922.     MISC_RANGE_CHECK( x );
  923.     if (uniform_size == 0)
  924.     MISC_SET_SLOT_MEMBER( x, n, max_size );
  925.     }
  926.  
  927.  
  928. //-----------------------------------------------------------------------------
  929. // SIZING
  930. //-----------------------------------------------------------------------------
  931. MiscTableSizing MiscTableBorder::getSizing( MiscCoord_V x ) const
  932.     {
  933.     MISC_RANGE_CHECK( x );
  934.     if (uniform_size != 0)
  935.     return MISC_NUSER_NSPRINGY_SIZING;
  936.     return MISC_SLOT_MEMBER( x, sizing );
  937.     }
  938.  
  939. void MiscTableBorder::setSizing( MiscCoord_V x, MiscTableSizing n )
  940.     {
  941.     MISC_RANGE_CHECK( x );
  942.     MISC_ENUM_CHECK( n, MISC_MAX_SIZING );
  943.     if (uniform_size == 0)
  944.     {
  945.     bool was_springy = ::isSpringy(getSizing(x));
  946.     bool is_springy = ::isSpringy(n);
  947.     if (was_springy != is_springy)
  948.         {
  949.         if (was_springy)
  950.         num_springy--;
  951.         else
  952.         num_springy++;
  953.         needs_recalc = true;
  954.         }
  955.     MISC_SET_SLOT_MEMBER( x, n, sizing );
  956.     }
  957.     }
  958.  
  959.  
  960. //-----------------------------------------------------------------------------
  961. // TITLES
  962. //-----------------------------------------------------------------------------
  963. void MiscTableBorder::dealloc_titles()
  964.     {
  965.     if (titles != 0)
  966.     {
  967.     for (int i = 0;  i < num_slots;  i++)
  968.         if (titles[i] != 0)
  969.         [titles[i] release];
  970.     free( titles );
  971.     titles = 0;
  972.     }
  973.     }
  974.  
  975. void MiscTableBorder::alloc_titles()
  976.     { titles = (NSString**) do_alloc_init( sizeof(*titles) ); }
  977.  
  978. bool MiscTableBorder::setTitle_P( MiscCoord_P x, NSString* s )
  979.     {
  980.     bool changed = false;
  981.     if (getTitleMode() == MISC_CUSTOM_TITLE)
  982.     {
  983.     MISC_RANGE_CHECK( x );
  984.     NSString* const t = (titles ? titles[x] : 0);
  985.     if (t != 0 || s != 0)
  986.         {
  987.         if (t == 0 || s == 0 || ![t isEqualToString:s])
  988.         {
  989.         if (t != 0) [t autorelease];
  990.         if (titles == 0) alloc_titles();
  991.         titles[x] = [s copy];
  992.         changed = true;
  993.         }
  994.         }
  995.     }
  996.     return changed;
  997.     }
  998.  
  999. static inline void prepend_char( NSMutableString* s, char const c )
  1000.     {
  1001.     NSString* cs = [NSString stringWithCString:&c length:1];
  1002.     [s insertString:cs atIndex:0];
  1003.     }
  1004.  
  1005. static NSString* alpha_title( int x )
  1006.     {
  1007.     NSMutableString* s = [[[NSMutableString alloc] init] autorelease];
  1008.     if (x >= 26)
  1009.     {
  1010.     do  {
  1011.         prepend_char( s, (x % 26) + 'A' );
  1012.         x /= 26;
  1013.         }
  1014.     while (x >= 26);
  1015.     x--;
  1016.     }
  1017.     prepend_char( s, x + 'A' );
  1018.     return s;
  1019.     }
  1020.  
  1021. NSString* MiscTableBorder::getTitle_P( MiscCoord_P x ) const
  1022.     {
  1023.     MISC_RANGE_CHECK( x );
  1024.     NSString* s = @"";
  1025.     switch (title_mode)
  1026.     {
  1027.     case MISC_NO_TITLE:
  1028.         break;
  1029.     case MISC_NUMBER_TITLE:
  1030.         s = [[NSNumber numberWithInt:x + 1] description];
  1031.         break;
  1032.     case MISC_ALPHA_TITLE:
  1033.         s = alpha_title(x);
  1034.         break;
  1035.     case MISC_CUSTOM_TITLE:
  1036.         s = (titles != 0) ? (titles[x] != 0 ? titles[x] : @"") : @"";
  1037.         break;
  1038.     case MISC_DELEGATE_TITLE:
  1039.         s = [owner border:type getDelegateSlotTitle:x];
  1040.         break;
  1041.     }
  1042.     return s;
  1043.     }
  1044.  
  1045. bool MiscTableBorder::setTitleMode( MiscTableTitleMode x )
  1046.     {
  1047.     MISC_ENUM_CHECK( x, MISC_MAX_TITLE );
  1048.     bool const changed = (title_mode != x);
  1049.     if (changed)
  1050.     {
  1051.     dealloc_titles();
  1052.     title_mode = x;
  1053.     }
  1054.     return changed;
  1055.     }
  1056.  
  1057.  
  1058. //-----------------------------------------------------------------------------
  1059. // TAGS
  1060. //-----------------------------------------------------------------------------
  1061. void MiscTableBorder::alloc_tags()
  1062.     {
  1063.     tags = (int*) do_alloc_init( sizeof(*tags) );
  1064.     if (def_tag != 0)
  1065.     for (int i = 0;     i < num_slots;     i++)
  1066.         tags[i] = def_tag;
  1067.     }
  1068.  
  1069. void MiscTableBorder::setTag_P( MiscCoord_P x, int n )
  1070.     {
  1071.     MISC_RANGE_CHECK( x );
  1072.     if (tags == 0) alloc_tags();
  1073.     tags[x] = n;
  1074.     }
  1075.  
  1076. int MiscTableBorder::getTag_P( MiscCoord_P x ) const
  1077.     {
  1078.     MISC_RANGE_CHECK( x );
  1079.     return (tags != 0) ? tags[x] : def_tag;
  1080.     }
  1081.  
  1082.  
  1083. //-----------------------------------------------------------------------------
  1084. // STYLES
  1085. //-----------------------------------------------------------------------------
  1086. void MiscTableBorder::alloc_styles()
  1087.     {
  1088.     styles = (MiscTableCellStyle*) do_alloc_init( sizeof(*styles) );
  1089.     if ((int) def_style != 0)
  1090.     for (int i = 0;     i < num_slots;     i++)
  1091.         styles[i] = def_style;
  1092.     }
  1093.  
  1094. void MiscTableBorder::setStyle_P( MiscCoord_P x, MiscTableCellStyle n )
  1095.     {
  1096.     MISC_RANGE_CHECK( x );
  1097.     MISC_ENUM_CHECK( n, MISC_TABLE_CELL_MAX );
  1098.     if (styles == 0) alloc_styles();
  1099.     styles[x] = n;
  1100.     }
  1101.  
  1102. MiscTableCellStyle MiscTableBorder::getStyle_P( MiscCoord_P x ) const
  1103.     {
  1104.     MISC_RANGE_CHECK( x );
  1105.     return (styles != 0) ? styles[x] : def_style;
  1106.     }
  1107.  
  1108.  
  1109. //-----------------------------------------------------------------------------
  1110. // PROTOTYPES
  1111. //-----------------------------------------------------------------------------
  1112. void MiscTableBorder::alloc_prototypes()
  1113.     { prototypes = (id*) do_alloc_init( sizeof(*prototypes) ); }
  1114.  
  1115. id MiscTableBorder::new_prototype( MiscCoord_P x )
  1116.     {
  1117.     NSZone* const zone = [owner zone];
  1118.     id p = 0;
  1119.     switch (getStyle_P(x))
  1120.     {
  1121.     case MISC_TABLE_CELL_TEXT:
  1122.         p = [[MiscTableCell allocWithZone:zone] initTextCell:@""];
  1123.         break;
  1124.     case MISC_TABLE_CELL_IMAGE:
  1125.         p = [[MiscTableCell allocWithZone:zone] initImageCell:
  1126.             [[NSImage alloc] initWithSize:NSZeroSize]];
  1127.         break;
  1128.     case MISC_TABLE_CELL_BUTTON:
  1129.         p = [[NSButtonCell allocWithZone:zone] initTextCell:@""];
  1130.         break;
  1131.     case MISC_TABLE_CELL_CALLBACK:
  1132.         p = [[owner border:type getDelegateSlotPrototype:x] retain];
  1133.         break;
  1134.     }
  1135.     return [p autorelease];
  1136.     }
  1137.  
  1138. id MiscTableBorder::getPrototype_P( MiscCoord_P x )
  1139.     {
  1140.     MISC_RANGE_CHECK( x );
  1141.     if (prototypes == 0)
  1142.     alloc_prototypes();
  1143.     id p = prototypes[x];
  1144.     if (p == 0)
  1145.     p = prototypes[x] = [new_prototype(x) retain];
  1146.     return p;
  1147.     }
  1148.  
  1149. void MiscTableBorder::setPrototype_P( MiscCoord_P x, id n )
  1150.     {
  1151.     MISC_RANGE_CHECK( x );
  1152.     if (prototypes == 0) alloc_prototypes();
  1153.     if (prototypes[x] != 0)
  1154.     [prototypes[x] autorelease];
  1155.     prototypes[x] = n;
  1156.     }
  1157.  
  1158.  
  1159. //-----------------------------------------------------------------------------
  1160. // SORT FUNCTIONS
  1161. //-----------------------------------------------------------------------------
  1162. void MiscTableBorder::alloc_sort_funcs()
  1163.     {
  1164.     sort_funcs = (MiscCompareEntryFunc*) do_alloc_init( sizeof(*sort_funcs) );
  1165.     }
  1166.  
  1167. void MiscTableBorder::setSortFunc_P( MiscCoord_P x, MiscCompareEntryFunc n )
  1168.     {
  1169.     MISC_RANGE_CHECK( x );
  1170.     if (sort_funcs == 0) alloc_sort_funcs();
  1171.     sort_funcs[x] = n;
  1172.     }
  1173.  
  1174. MiscCompareEntryFunc MiscTableBorder::getSortFunc_P( MiscCoord_P x ) const
  1175.     {
  1176.     MISC_RANGE_CHECK( x );
  1177.     return (sort_funcs != 0) ? sort_funcs[x] : 0;
  1178.     }
  1179.  
  1180.  
  1181. //-----------------------------------------------------------------------------
  1182. // SORT INFO (TYPE + DIR)
  1183. //-----------------------------------------------------------------------------
  1184. void MiscTableBorder::alloc_sort_info()
  1185.     { sort_info = (int*) do_alloc_init( sizeof(*sort_info) ); }
  1186.  
  1187. void MiscTableBorder::setSortType_P( MiscCoord_P x, MiscSortType n )
  1188.     {
  1189.     MISC_RANGE_CHECK( x );
  1190.     MISC_ENUM_CHECK( n, MISC_SORT_TYPE_MAX );
  1191.     if (sort_info == 0) alloc_sort_info();
  1192.     int const z = sort_info[x];            // Preserve direction.
  1193.     sort_info[x] = ((n << 1) | (z & 1));
  1194.     }
  1195.  
  1196. MiscSortType MiscTableBorder::getSortType_P( MiscCoord_P x ) const
  1197.     {
  1198.     MISC_RANGE_CHECK( x );
  1199.     return (sort_info != 0) ? MiscSortType( sort_info[x] >> 1 ) :
  1200.                 MISC_SORT_STRING_CASE_INSENSITIVE;
  1201.     }
  1202.  
  1203. void MiscTableBorder::setSortDirection_P( MiscCoord_P x, MiscSortDirection n )
  1204.     {
  1205.     MISC_RANGE_CHECK( x );
  1206.     MISC_ENUM_CHECK( n, MISC_SORT_DIR_MAX );
  1207.     if (sort_info == 0) alloc_sort_info();
  1208.     int const z = sort_info[x];            // Preserve type.
  1209.     sort_info[x] = ((z & ~1) | (n & 1));
  1210.     }
  1211.  
  1212. MiscSortDirection MiscTableBorder::getSortDirection_P( MiscCoord_P x ) const
  1213.     {
  1214.     MISC_RANGE_CHECK( x );
  1215.     return (sort_info != 0) ? MiscSortDirection( sort_info[x] & 1 ) :
  1216.         MISC_SORT_ASCENDING;
  1217.     }
  1218.  
  1219. void MiscTableBorder::clearSortDirection()    // Set all slots to ascending.
  1220.     {
  1221.     if (sort_info != 0)
  1222.     {
  1223.     int const* const plim = sort_info + count();
  1224.     for (int* p = sort_info; p < plim; p++)
  1225.         *p &= ~1;
  1226.     }
  1227.     }
  1228.  
  1229.  
  1230. //-----------------------------------------------------------------------------
  1231. // DESTRUCTOR / CONSTRUCTOR
  1232. //-----------------------------------------------------------------------------
  1233. MiscTableBorder::~MiscTableBorder()    { emptyAndFree(); }
  1234. MiscTableBorder::MiscTableBorder( MiscBorderType x )
  1235.     {
  1236.     MISC_ENUM_CHECK( x, MISC_MAX_BORDER );
  1237.     memset( this, 0, sizeof(*this) );
  1238.     type = x;
  1239.     clearSelectedSlot();
  1240.     clearCursor();
  1241.     clearClickedSlot();
  1242.  
  1243.     if (type == MISC_ROW_BORDER)
  1244.     {
  1245.     uniform_size    = MISC_FRAME_HEIGHT;
  1246.     def_slot.offset = 0;
  1247.     def_slot.size    = uniform_size;
  1248.     def_slot.min_size = 10;
  1249.     def_slot.max_size = MISC_MAX_PIXELS_SIZE;
  1250.     def_slot.sizing = MISC_NUSER_NSPRINGY_SIZING;
  1251.     title_mode    = MISC_NUMBER_TITLE;
  1252.     draggable    = false;
  1253.     modifier_drag    = true;
  1254.     sizeable    = false;
  1255.     selectable    = true;
  1256.     }
  1257.     else
  1258.     {
  1259.     def_slot.offset = 0;
  1260.     def_slot.size    = 80;
  1261.     def_slot.min_size = 10;
  1262.     def_slot.max_size = MISC_MAX_PIXELS_SIZE;
  1263.     def_slot.sizing = MISC_USER_NSPRINGY_SIZING;
  1264.     uniform_size    = 0;
  1265.     title_mode    = MISC_CUSTOM_TITLE;
  1266.     draggable    = true;
  1267.     modifier_drag    = false;
  1268.     sizeable    = true;
  1269.     selectable    = false;
  1270.     }
  1271.     min_uniform_size = MISC_MIN_PIXELS_SIZE;
  1272.     max_uniform_size = MISC_MAX_PIXELS_SIZE;
  1273.     }
  1274.