home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / Kibitz / BoardSlider.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-01  |  17.5 KB  |  651 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        boardslider.c
  5. ** Written by:  Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved. */
  9.  
  10. /* This is a custom slider for Kibitz.  It sends AppleEvents to the opponent,
  11. ** if there is one, so that the remote board is also scrolled.  It sends a
  12. ** maximum of 1 a second, so that they will be able to be processed without
  13. ** stacking up at the other end.
  14. **
  15. ** There is a custom cdef for this code.  All it does is jump to this code.
  16. ** There is a really good reason for this, which is most obscure.  It is
  17. ** possible that, when you choose an opponent machine, you choose your own
  18. ** machine.  This is fully supported in Kibitz.  As a matter of fact, only
  19. ** theMakeTarget code "knows" that it is the same machine.  The rest of the
  20. ** application is completely ignorant of this fact.  When the user clicks
  21. ** on the slider, the control manager locks down the cdef, and then calls it.
  22. ** When the custom cdef returns to the control manager, the cdef is unlocked.
  23. ** This all seems very reasonable.  However, in the case that you are sending
  24. ** a game to the same machine, I send an AppleEvent, which causes an update
  25. ** on the slider.  This update is handled by the control manager calling the
  26. ** cdef.  Of course, it locks it down, and then when the control manager is
  27. ** returned to, it unlocks it.  BUT WAIT!!  We are still tracking the slider
  28. ** that caused the AppleEvent to be sent, which in turn caused the slider of
  29. ** another window to update.  This is true.  It is also true that the cdef
  30. ** is now UNLOCKED!  And with AppleEvents using memory in a healthy way, it
  31. ** is very probable that the cdef will  move.  It isn't a good idea to rts
  32. ** to code that has moved.  This is why the cdef jumps to the code in the
  33. ** application.  We never return to the (potentially unlocked) cdef.  We
  34. ** return straight to the control manager.  Ugly problem, huh? */
  35.  
  36.  
  37.  
  38. /*****************************************************************************/
  39.  
  40.  
  41.  
  42. #define kCapHeight        11
  43. #define kThumbHeight    7
  44. #define kThumbOffset    12
  45. #define kSliderWidth    13
  46.  
  47. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  48. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  49. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  50.  
  51. #ifndef __GWLAYERS__
  52. #include <GWLayers.h>
  53. #endif
  54.  
  55. #ifndef __RESOURCES__
  56. #include <Resources.h>
  57. #endif
  58.  
  59. #ifndef __TOOLUTILS__
  60. #include <ToolUtils.h>
  61. #endif
  62.  
  63. #ifndef __UTILITIES__
  64. #include <Utilities.h>
  65. #endif
  66.  
  67.  
  68.  
  69. /*****************************************************************************/
  70.  
  71.  
  72.  
  73. #ifdef powerc
  74. #pragma options align=mac68k
  75. #endif
  76. typedef struct cdefRsrcJMP {
  77.     long    jsrInst;
  78.     long    moveInst;
  79.     short    jmpInst;
  80.     long    jmpAddress;
  81. } cdefRsrcJMP;
  82. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  83. #if defined(powerc) || defined(__powerc)
  84. #pragma options align=reset
  85. #endif
  86.  
  87. #ifdef powerc
  88. #pragma options align=mac68k
  89. #endif
  90. typedef struct thumbCntlParams {
  91.     Rect    limitRect;
  92.     Rect    slopRect;
  93.     short    axis;
  94. } thumbCntlParams;
  95. #if defined(powerc) || defined(__powerc)
  96. #pragma options align=reset
  97. #endif
  98.  
  99.  
  100.  
  101. static void            BoardDrawCIcon(CIconHandle iconHndl, short hloc, short vloc);
  102. static pascal long    BoardSliderCtl(short varCode, ControlHandle ctl, short msg, long parm);
  103. static void            BoardSliderUpdate(ControlHandle ctl, short hiliteCap);
  104. static Rect            CalcSliderRect(ControlHandle ctl);
  105. static void            TrackSlider(ControlHandle ctl, Point origMouseLoc);
  106.  
  107.  
  108.  
  109. /*****************************************************************************/
  110.  
  111.  
  112.  
  113. /* Given a file reference, adjust the slider to reflect the position
  114. ** of the chessboard. */
  115.  
  116. #pragma segment Controls
  117. void    AdjustGameSlider(FileRecHndl frHndl)
  118. {
  119.     ControlHandle    ctl;
  120.     short            val, max;
  121.     WindowPtr        oldPort;
  122.  
  123.     ctl = (*frHndl)->doc.gameSlider;
  124.     val = (*frHndl)->doc.gameIndex;
  125.     max = (*frHndl)->doc.numGameMoves;
  126.         /* Get the info we need. */
  127.  
  128.     (*ctl)->contrlMax   = max;
  129.     (*ctl)->contrlValue = val;
  130.     oldPort = SetFilePort(frHndl);
  131.     BoardSliderUpdate(ctl, -1);
  132.         /* Change the slider value and show the result. */
  133.  
  134.     SetPort(oldPort);
  135. }
  136.  
  137.  
  138.  
  139. /*****************************************************************************/
  140.  
  141.  
  142.  
  143. #pragma segment Controls
  144. ControlHandle    BoardSliderNew(WindowPtr window)
  145. {
  146.     WindowPtr        oldPort;
  147.     FileRecHndl        frHndl;
  148.     Rect            boardRect;
  149.     ControlHandle    sliderCtl;
  150.     cdefRsrcJMPHndl    cdefRsrc;
  151.     static    ControlDefUPP    boardSliderCtlUPP = nil;
  152.  
  153.     GetPort(&oldPort);
  154.     SetPort(window);
  155.  
  156.     frHndl = (FileRecHndl)GetWRefCon(window);
  157.  
  158.     boardRect = BoardRect();
  159.     boardRect.left    = boardRect.right + 4;
  160.     boardRect.right   = boardRect.left + 13;
  161.     boardRect.top    += 4;
  162.     boardRect.bottom -= 4;
  163.  
  164.     cdefRsrc = (cdefRsrcJMPHndl)GetResource('CDEF', (rSliderCtl / 16));
  165.     if (!boardSliderCtlUPP)
  166.         boardSliderCtlUPP = NewControlDefProc(BoardSliderCtl);
  167.     (*cdefRsrc)->jmpAddress = (long)boardSliderCtlUPP;
  168.     FlushInstructionCache();
  169.         /* Make sure that instruction caches don't kill us. */
  170.  
  171.     sliderCtl = NewControl(window, &boardRect, "\p", true, 0, 0, 0,
  172.                            rSliderCtl, (long)frHndl);
  173.  
  174.     return(sliderCtl);
  175. }
  176.  
  177.  
  178.  
  179. /*****************************************************************************/
  180.  
  181.  
  182.  
  183. #pragma segment Controls
  184. pascal long    BoardSliderCtl(short varCode, ControlHandle ctl, short msg, long parm)
  185. {
  186. #ifndef __MWERKS__
  187. #pragma unused (varCode)
  188. #endif
  189.  
  190.     Rect            viewRect, sliderRect;
  191.     thumbCntlParams    *tcp;
  192.     static Point    lastClick;
  193.  
  194.     viewRect = (*ctl)->contrlRect;
  195.  
  196.     switch (msg) {
  197.         case drawCntl:
  198.             BoardSliderUpdate(ctl, -1);
  199.             break;
  200.  
  201.         case testCntl:
  202.             if ((*ctl)->contrlHilite == 255) return(0);
  203.             if (!(*ctl)->contrlMax) return(0);
  204.             if (PtInRect(*(Point *)&parm, &viewRect)) {
  205.                 lastClick = *((Point *)&parm);
  206.                 return(inThumb);
  207.             }        /* Everything is the thumb.  The "thumb" routine will figure
  208.                     ** out what part it is.  Since this is a very specific control,
  209.                     ** we can get away with this simplification. */
  210.             return(0);
  211.  
  212.         case calcCRgns:
  213.         case calcCntlRgn:
  214.             if (msg == calcCRgns) parm &= 0x00FFFFFF;
  215.             RectRgn((RgnHandle)parm, &viewRect);
  216.             break;
  217.  
  218.         case initCntl:
  219.             break;
  220.  
  221.         case dispCntl:
  222.             break;
  223.  
  224.         case posCntl:
  225.             break;
  226.  
  227.         case thumbCntl:
  228.             sliderRect = (*ctl)->contrlRect;
  229.             tcp = (thumbCntlParams *)parm;
  230.             tcp->limitRect = sliderRect;
  231.             InsetRect(&sliderRect, -64, -64);
  232.             tcp->slopRect = sliderRect;
  233.             tcp->axis = 2;
  234. //            TrackSlider(ctl, lastClick);
  235.             break;
  236.  
  237.         case dragCntl:
  238.             TrackSlider(ctl, lastClick);
  239.             return(true);
  240.  
  241.         case autoTrack:
  242.             break;
  243.  
  244.         default:
  245.             break;
  246.     }
  247.  
  248.     return(0);
  249. }
  250.  
  251.  
  252.  
  253. /*****************************************************************************/
  254.  
  255.  
  256.  
  257. #pragma segment Controls
  258. void    BoardSliderUpdate(ControlHandle ctl, short hiliteCap)
  259. {
  260.     Rect        ctlRect, workRect, sliderRect;
  261.     FileRecHndl    frHndl;
  262.     Boolean        active;
  263.     short        i, j, thumbColor;
  264.     RgnHandle    origClipRgn, clipRgn, workRgn;
  265.     CIconHandle    icons[7];
  266.  
  267.     /* We use color icons here for the various slider parts.  This is so that we
  268.     ** can take advantage of the depth of monitors.  I use icons here so that I
  269.     ** can do a single plot of an icon if the delta of the thumb is -12 to 12.
  270.     ** (The thumb is in the center of an icon, and 12 pixels above and below the
  271.     ** icon, I have slider bar.  Use RedEdit to check it out.)  This technique
  272.     ** gives a very smooth appearance when the thumb slides.  There is no flash.
  273.     ** For deltas greater than +-12, I redraw the slider without the thumb, and
  274.     ** then draw the thumb in the new position.  Since the thumb is moving a lot
  275.     ** anyway, this doesn't show up as a flicker.  There is no overlap in the
  276.     ** old and new positions for a big delta. */
  277.  
  278.     ctlRect = (*ctl)->contrlRect;
  279.     frHndl  = (FileRecHndl)GetControlReference(ctl);
  280.  
  281.     for (i = 0; i < 7; i++) icons[i] = ReadCIcon(i + rSliderBase);
  282.  
  283.     origClipRgn = NewRgn();
  284.     GetClip(origClipRgn);
  285.  
  286.     clipRgn = NewRgn();
  287.  
  288.     for (i = 0; i < 2; i++) {        /* Draw the arrow parts first. */
  289.         j = i;
  290.         if (hiliteCap == i) j += 5;
  291.         workRect = ctlRect;
  292.         if (!i)
  293.             workRect.bottom = workRect.top + kCapHeight;
  294.         else
  295.             workRect.top = workRect.bottom - kCapHeight;
  296.         RectRgn(clipRgn, &workRect);
  297.         SetClip(clipRgn);
  298.             /* Clip out the area outside the arrow part. */
  299.         BoardDrawCIcon(icons[j], workRect.left, workRect.top);
  300.             /* Draw the arrow part. */
  301.     }
  302.  
  303.     ctlRect.top    += kCapHeight;
  304.     ctlRect.bottom -= kCapHeight;
  305.     RectRgn(clipRgn, &ctlRect);
  306.     SetClip(clipRgn);
  307.         /* Clip out everything except the slider bar area. */
  308.  
  309.     active  = ((*ctl)->contrlOwner == FrontWindow());
  310.     if ((*ctl)->contrlHilite == 255) active = false;
  311.     if (!(*ctl)->contrlMax) active = false;
  312.  
  313.     if (active) {        /* If control active, draw the thumb. */
  314.         sliderRect = CalcSliderRect(ctl);
  315.         thumbColor = (((*ctl)->contrlValue & 0x01) ^ (*frHndl)->doc.startColor);
  316.         BoardDrawCIcon(icons[3 + thumbColor],
  317.                        sliderRect.left, sliderRect.top - kThumbOffset);
  318.         workRgn = NewRgn();
  319.         RectRgn(workRgn, &sliderRect);
  320.         DiffRgn(clipRgn, workRgn, clipRgn);
  321.         SetClip(clipRgn);
  322.         DisposeRgn(workRgn);
  323.             /* Now that the thumb is drawn, protect it by clipping it out. */
  324.     }
  325.  
  326.     for (i = ctlRect.top; i < ctlRect.bottom; i += 32)
  327.         BoardDrawCIcon(icons[2], ctlRect.left, i);
  328.             /* Draw the slider bar portion. */
  329.  
  330.     /* It is now completely drawn.  Clean up and get out. */
  331.  
  332.     SetClip(origClipRgn);
  333.     DisposeRgn(clipRgn);
  334.     DisposeRgn(origClipRgn);
  335.  
  336.     for (i = 0; i < 7; i++) KillCIcon(icons[i]);
  337. }
  338.  
  339.  
  340.  
  341. /*****************************************************************************/
  342.  
  343.  
  344.  
  345. #pragma segment Controls
  346. void    BoardDrawCIcon(CIconHandle iconHndl, short hloc, short vloc)
  347. {
  348.     Rect    iconRect;
  349.  
  350.     iconRect.right  = (iconRect.left = hloc) + 32;
  351.     iconRect.bottom = (iconRect.top  = vloc) + 32;
  352.  
  353.     DrawCIcon(iconHndl, iconRect);
  354. }
  355.  
  356.  
  357.  
  358. /*****************************************************************************/
  359.  
  360.  
  361.  
  362. #pragma segment Controls
  363. Rect    CalcSliderRect(ControlHandle ctl)
  364. {
  365.     Rect    ctlRect, sliderRect;
  366.     short    max, val;
  367.     long    calc;
  368.  
  369.     ctlRect = (*ctl)->contrlRect;
  370.     ctlRect.top    += kCapHeight;
  371.     ctlRect.bottom -= kCapHeight;
  372.     max = (*ctl)->contrlMax;
  373.     val = (*ctl)->contrlValue;
  374.  
  375.     calc = ctlRect.bottom - ctlRect.top - kThumbHeight;
  376.     calc *= val;
  377.     if (max) calc /= max;
  378.     sliderRect.top    = ctlRect.top + calc,
  379.     sliderRect.left   = ctlRect.left;
  380.     sliderRect.bottom = sliderRect.top + kThumbHeight;
  381.     sliderRect.right  = ctlRect.right;
  382.  
  383.     return(sliderRect);
  384. }
  385.  
  386.  
  387.  
  388. /*****************************************************************************/
  389.  
  390.  
  391.  
  392. #pragma segment Controls
  393. void    TrackSlider(ControlHandle ctl, Point origMouseLoc)
  394. {
  395.     CIconHandle    icons[7];
  396.     WindowPtr    oldPort;
  397.     Rect        ctlRect, sliderRange, slopRect, sliderRect, capRect, pgRect;
  398.     FileRecHndl    frHndl;
  399.     RgnHandle    origClipRgn, clipRgn, workRgn;
  400.     short        i, max, val, origGameIndex, ovloc, voffset, vloc, delta, hiliteCap;
  401.     Boolean        twoPlayer, hiliteOn, doPgScroll;
  402.     long        origTick, tick, calc;
  403.     Point        lastMouseLoc, mouseLoc;
  404.  
  405.     /* Get everything we need set up. */
  406.  
  407.     origTick = tick = TickCount();
  408.  
  409.     for (i = 2; i < 5; i++) icons[i] = ReadCIcon(i + rSliderBase);
  410.  
  411.     frHndl  = (FileRecHndl)GetControlReference(ctl);
  412.     oldPort = SetFilePort(frHndl);
  413.     ctlRect = (*ctl)->contrlRect;
  414.  
  415.     origClipRgn = NewRgn();
  416.     GetClip(origClipRgn);
  417.  
  418.     clipRgn = NewRgn();
  419.     workRgn = NewRgn();
  420.  
  421.     origGameIndex = (*frHndl)->doc.gameIndex;
  422.     max = (*ctl)->contrlMax;
  423.  
  424.     twoPlayer  = (*frHndl)->doc.twoPlayer;
  425.     sliderRect = CalcSliderRect(ctl);
  426.  
  427.     /* That ought to be enough setup. */
  428.  
  429.     if (PtInRect(origMouseLoc, &sliderRect)) {    /* If they are on the thumb... */
  430.  
  431.         ctlRect.top    += kCapHeight;            /* Protect the arrow parts. */
  432.         ctlRect.bottom -= kCapHeight;
  433.         RectRgn(clipRgn, &ctlRect);
  434.         SetClip(clipRgn);
  435.  
  436.         sliderRange = ctlRect;                    /* Calc area thumb can move. */
  437.         sliderRange.bottom -= kThumbHeight;        /* Count height of thumb against range. */
  438.  
  439.         slopRect = sliderRange;                    /* Give the user some slop. */
  440.         InsetRect(&slopRect, -20, -20);
  441.  
  442.         lastMouseLoc = origMouseLoc;
  443.         voffset = lastMouseLoc.v - sliderRect.top;
  444.         ovloc   = lastMouseLoc.v - voffset;
  445.  
  446.         while (StillDown()) {
  447.  
  448.             if (tick + 30 < TickCount()) {        /* Send max 1 AppleEvent per 1/2 sec. */
  449.                 tick = TickCount();
  450.                 if (twoPlayer) SendGame(frHndl, kScrolling, nil);
  451.             }
  452.  
  453.             GetMouse(&mouseLoc);
  454.             if (!EqualPt(mouseLoc, lastMouseLoc)) {        /* The mouse has moved. */
  455.  
  456.                 if (!PtInRect(mouseLoc, &slopRect)) mouseLoc = origMouseLoc;
  457.                     /* Outside slopRect, so snap back to the original position. */
  458.  
  459.                 vloc = mouseLoc.v - voffset;
  460.                 if (vloc < sliderRange.top)    vloc = sliderRange.top;
  461.                 if (vloc > sliderRange.bottom) vloc = sliderRange.bottom;
  462.                 delta = vloc - ovloc;
  463.                     /* The delta tells us how much the thumb moved. */
  464.  
  465.                 if (
  466.                     (delta < -((32 - kThumbHeight) / 2)) ||
  467.                     (delta >  ((32 - kThumbHeight) / 2))
  468.                 ) {
  469.                     for (i = ctlRect.top; i < ctlRect.bottom; i += 32)
  470.                         BoardDrawCIcon(icons[2], ctlRect.left, i);
  471.                             /* The thumb moved too far for a single plot to cover
  472.                             ** up the old position, so clear the old thumb. */
  473.                 }
  474.  
  475.                 calc  = max + 1;        /* Force below math to be with longs. */
  476.                 calc *= (vloc - sliderRange.top);
  477.                     /* We use max + 1 because there is one more game
  478.                     ** move position than moves in the game.  This is
  479.                     ** because we can position in front of the first move,
  480.                     ** as well as after the last move. */
  481.                 calc /= (sliderRange.bottom - sliderRange.top);
  482.  
  483.                 val = calc;
  484.                 if (val > max) val = max;
  485.                 if (val < 0)   val = 0;
  486.                 if (delta)
  487.                     BoardDrawCIcon(icons[3 + ((val + (*frHndl)->doc.startColor) & 0x01)],
  488.                                    ctlRect.left, vloc - kThumbOffset);
  489.                                         /* The thumb is now updated. */
  490.  
  491.                 SetClip(origClipRgn);
  492.                 RepositionBoard(frHndl, val, true);
  493.                     /* We set the clipRgn back to the original so the board
  494.                     ** can update.  (Pretty boring if it doesn't. */
  495.  
  496.                 SetClip(clipRgn);
  497.                     /* Back to our normally scheduled program... */
  498.  
  499.                 lastMouseLoc = mouseLoc;
  500.                 (*ctl)->contrlValue = val;
  501.                 ovloc = vloc;
  502.             }
  503.         }
  504.     }
  505.  
  506.     else {        /* We missed the thumb.  See if we hit an arrow part... */
  507.         delta = hiliteOn = 0;
  508.         capRect = ctlRect;
  509.         capRect.bottom = ctlRect.top + kCapHeight;
  510.         if (PtInRect(origMouseLoc, &capRect)) {
  511.             delta = -1;
  512.             hiliteCap = 0;
  513.         }
  514.         else {
  515.             capRect = ctlRect;
  516.             capRect.top = ctlRect.bottom - kCapHeight;
  517.             if (PtInRect(origMouseLoc, &capRect)) {
  518.                 delta = 1;
  519.                 hiliteCap = 1;
  520.             }
  521.         }
  522.  
  523.         if (delta) {    /* We hit an arrow, and there is a change to do... */
  524.             do {
  525.                 if (tick + 30 < TickCount()) {
  526.                     tick = TickCount();
  527.                     if (twoPlayer) SendGame(frHndl, kScrolling, nil);
  528.                 }
  529.                 GetMouse(&mouseLoc);
  530.                 if (PtInRect(mouseLoc, &capRect)) {        /* Still in arrow... */
  531.                     val = (*ctl)->contrlValue + delta;
  532.                     if ((val >= 0) && (val <= max)) {    /* Still scrolling... */
  533.                         hiliteOn = true;
  534.                         (*ctl)->contrlValue = val;
  535.                         BoardSliderUpdate(ctl, hiliteCap);
  536.                         SetClip(origClipRgn);
  537.                         if (RepositionBoard(frHndl, val, true)) SetClip(clipRgn);
  538.                     }
  539.                     else {        /* Scrolled as far as we can go, so unhilite arrow. */
  540.                         if (hiliteOn) {
  541.                             BoardSliderUpdate(ctl, -1);
  542.                             hiliteOn = false;
  543.                         }
  544.                     }
  545.                 }
  546.                 else {        /* Outside arrow, so unhilite it. */
  547.                     if (hiliteOn) {
  548.                         BoardSliderUpdate(ctl, -1);
  549.                         hiliteOn = false;
  550.                     }
  551.                 }
  552.  
  553.                 while ((StillDown()) && (origTick + 20 > TickCount())) {};
  554.                     /* Don't go too fast. */
  555.  
  556.             } while (StillDown());
  557.         }
  558.  
  559.         else {
  560.  
  561.             pgRect = ctlRect;
  562.             pgRect.top    += kCapHeight;
  563.             pgRect.bottom -= kCapHeight;
  564.             if (PtInRect(origMouseLoc, &pgRect)) {        /* If in the page area... */
  565.                 delta = (origMouseLoc.v < sliderRect.top) ? -2 : 2;
  566.                 InsetRect(&pgRect, -10, -10);
  567.                 do {
  568.                     if (tick + 30 < TickCount()) {
  569.                         tick = TickCount();
  570.                         if (twoPlayer) SendGame(frHndl, kScrolling, nil);
  571.                     }
  572.                     GetMouse(&mouseLoc);
  573.                     if (PtInRect(mouseLoc, &pgRect)) {        /* Still in page area... */
  574.                         doPgScroll = false;
  575.                         sliderRect = CalcSliderRect(ctl);
  576.                         if ((delta == -2) && (mouseLoc.v < sliderRect.top))    doPgScroll = true;
  577.                         if ((delta == 2)  && (mouseLoc.v > sliderRect.bottom)) doPgScroll = true;
  578.                         if (doPgScroll) {
  579.                             val = (*ctl)->contrlValue + delta;
  580.                             if (val == -1)       val = 0;
  581.                             if (val == (max + 1)) val = max;
  582.                             if ((val >= 0) && (val <= max)) {    /* Still scrolling... */
  583.                                 (*ctl)->contrlValue = val;
  584.                                 BoardSliderUpdate(ctl, -1);
  585.                                 SetClip(origClipRgn);
  586.                                 if (RepositionBoard(frHndl, val, true)) SetClip(clipRgn);
  587.                             }
  588.                         }
  589.                     }
  590.  
  591.                     while ((StillDown()) && (origTick + 20 > TickCount())) {};
  592.                         /* Don't go too fast. */
  593.  
  594.                 } while (StillDown());
  595.             }
  596.         }
  597.     }
  598.  
  599.     SetClip(origClipRgn);
  600.     DisposeRgn(workRgn);
  601.     DisposeRgn(clipRgn);
  602.     DisposeRgn(origClipRgn);
  603.  
  604.     if (twoPlayer) SendGame(frHndl, kResync, nil);
  605.         /* Make sure that the result from the scroll isn't ignored.  The
  606.         ** opponent may ignore the events while we are scrolling, but not
  607.         ** when we are done. */
  608.  
  609.     BoardSliderUpdate(ctl, -1);
  610.         /* Snap the slider to a move position.  The user may have let go of the
  611.         ** slider at a position that doesn't map exactly to the game position. */
  612.  
  613.     for (i = 2; i < 5; i++) KillCIcon(icons[i]);
  614.  
  615.     SetPort(oldPort);
  616. }
  617.  
  618.  
  619.  
  620. /*****************************************************************************/
  621.  
  622.  
  623.  
  624. #pragma segment Controls
  625. Boolean    RepositionBoard(FileRecHndl frHndl, short newPos, Boolean update)
  626. {
  627.     short        oldPos, delta;
  628.  
  629.     oldPos = (*frHndl)->doc.gameIndex;
  630.     if (newPos == oldPos) return(false);
  631.  
  632.     delta  = (newPos > oldPos) ? 1 : -1;
  633.         /* We need to walk the board forward or backward delta number of moves. */
  634.  
  635.     for (; oldPos != newPos; oldPos += delta)
  636.         MakeMove(frHndl, delta, 0, 0);
  637.             /* Walk the board position one half-move forward or backward. */
  638.  
  639.     if (update) {
  640.         ImageDocument(frHndl, true);
  641.         DrawButtonTitle(frHndl, (*frHndl)->doc.twoPlayer);
  642.         UpdateGameStatus(frHndl);
  643.             /* Show the new board position. */
  644.     }
  645.  
  646.     return(true);
  647. }
  648.  
  649.  
  650.  
  651.