home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Code Resources / Jims CDEFs 1.50 / CDEF Source / source / cdefSpinner.c < prev    next >
Encoding:
Text File  |  1995-11-08  |  28.9 KB  |  1,000 lines  |  [TEXT/KAHL]

  1. //------------------------- © 1992-1995 by James G. Stout --------------------------
  2. // File        : cdefSpinner.c
  3. // Date        : 24 October 1992
  4. // Author    : Jim Stout
  5. //            :
  6. // Purpose    : a "Spinner Control" CDEF that allows adjustment of a
  7. //            : number via an up/down spinner - a "little arrow" control.
  8. //            :
  9. //            : see cdefSpinner.h for more detail and variation codes
  10. //            :
  11. //            : If you find a use for this, I'd love to know about it.  Bug reports
  12. //            : are always interesting.
  13. //            :
  14. //            : Internet    : JimS@WRQ.COM(work hours, PST)
  15. //            : AppleLink   : WRQ            (daily)
  16. //            : CompuServe  : 73240,2052    (weekly or so)
  17. //            : AOL         : JasG        (weekly or so)
  18. //            : eWorld      : Jim Stout    (weekly or so)
  19. //----------------------------------------------------------------------------------
  20. //#define _DEBUGCDEF 1
  21.  
  22. #include "fatCDEF.h"
  23.  
  24. #include <Controls.h>
  25. #include <Dialogs.h>
  26. #include <GestaltEqu.h>
  27. #include <LowMem.h>
  28. #include <QDOffscreen.h>
  29. #include <ToolUtils.h>
  30. #include <Types.h>
  31.  
  32. #include "cdefSpinner.h"
  33.  
  34. #include "grayCDEF.h"
  35. #include "colorCDEF.h"
  36. #include "miscCDEF.h"
  37. #include "qdCDEF.h"
  38.  
  39.  
  40. #ifdef _DEBUGCDEF
  41. pascal long CDmain (short, ControlHandle, short, long);
  42.  
  43. pascal long CDmain (short varCode, ControlHandle theCtl, short message, long param)
  44. #else
  45.  
  46. //==================================================================================
  47. // CDEF entry point
  48. //==================================================================================
  49.  
  50. pascal long main (short varCode, ControlHandle theCtl, short message, long param)
  51. #endif
  52. {
  53.     long        ret = 0L;
  54.     short        txF,txS;
  55.     GrafPtr        thisPort;
  56.     hSpinData    hCDEF;
  57.     SignedByte    cState, dState;
  58.         
  59. #include "fatEntry.c"
  60.     
  61.     cState = HGetState((Handle)theCtl);
  62.     HLock((Handle)theCtl);
  63.     
  64. //----------------------------------------------------------------------------------
  65. // make sure we've got the correct font…    
  66. //----------------------------------------------------------------------------------
  67.  
  68.     if(!(varCode & useWindFont)) {            // don't use the window font    
  69.         GetPort(&thisPort);
  70.         txF = thisPort->txFont;
  71.         txS = thisPort->txSize;
  72.         TextFont(LMGetSysFontFam());        // set system as current
  73.         TextSize(LMGetSysFontSize());
  74.     }
  75.  
  76. //----------------------------------------------------------------------------------
  77. // get our private data    
  78. //----------------------------------------------------------------------------------
  79.  
  80.     if((**theCtl).contrlData) {
  81.         dState = HGetState((**theCtl).contrlData);
  82.         HLock((**theCtl).contrlData);
  83.         hCDEF = (hSpinData)(**theCtl).contrlData;
  84.     }
  85.     
  86. //----------------------------------------------------------------------------------
  87. // handle the standard control messages    
  88. //----------------------------------------------------------------------------------
  89.  
  90.     switch(message) {
  91.         case initCntl:
  92.             doInit(theCtl, varCode);
  93.             if((**theCtl).contrlData) {
  94.                 dState = HGetState((**theCtl).contrlData);
  95.                 HLock((**theCtl).contrlData);
  96.             }
  97.         break;
  98.         case dispCntl:
  99.             doDisp(theCtl);
  100.         break;
  101.         case testCntl:        
  102.             if((**theCtl).contrlHilite != 0xFF)            // only if active
  103.                 ret = doTest (theCtl, varCode, param);
  104.         break;
  105.         case drawCntl:
  106.             if ((**theCtl).contrlVis != 0 &&
  107.                 ((WindowPeek)(**theCtl).contrlOwner)->visible) {
  108.                 doDraw(theCtl, varCode);
  109.             }
  110.         break;
  111.         case calcCRgns:
  112.             RectRgn((RgnHandle)(param & 0x7fffffffL), &(**theCtl).contrlRect);
  113.         break;
  114.         case calcCntlRgn:
  115.         case calcThumbRgn:
  116.             RectRgn((RgnHandle)(param), &(**theCtl).contrlRect);
  117.         break;
  118.     }
  119.         
  120. //----------------------------------------------------------------------------------
  121. // restore window font & size info    
  122. //----------------------------------------------------------------------------------
  123.     
  124.     if(!(varCode & useWindFont)) {
  125.         TextFont(txF);
  126.         TextSize(txS);
  127.     }
  128.  
  129.     if((**theCtl).contrlData)
  130.         HSetState((**theCtl).contrlData, dState);
  131.     HSetState((Handle)theCtl, cState);
  132.  
  133. #include "fatExit.c"
  134.  
  135.     return (ret);
  136. }
  137.  
  138. //==================================================================================
  139. //    initialize our private data and save in contrlData field.
  140. //==================================================================================
  141.  
  142. static void doInit (ControlHandle theCtl, short varCode)
  143. {
  144.     hSpinData    hCDEF;
  145.     
  146.     hCDEF = (hSpinData) NewHandle(sizeof(spinData));
  147.     if(hCDEF) {
  148.  
  149. //----------------------------------------------------------------------------------    
  150. //  If the control refCon is non-zero, the LoWord is the increment
  151. //    to use and the HiWord is a DITL item number for an editText item
  152. //    that we will set with the contrlValue in updateValue.
  153. //----------------------------------------------------------------------------------
  154.     
  155.         HLock((Handle)hCDEF);
  156.         
  157.         (**hCDEF).currIncr    = 1;
  158.         (**hCDEF).increment    = 1;
  159.         (**hCDEF).itemNum     = 0;
  160.         
  161.         if((**theCtl).contrlRfCon) {
  162.             (**hCDEF).increment = LoWord((**theCtl).contrlRfCon);
  163.             if((**hCDEF).increment < 1 ||
  164.                 (**hCDEF).increment > 1000) {                        
  165.                 (**hCDEF).increment = 1;
  166.             }
  167.             (**hCDEF).currIncr = (**hCDEF).increment;
  168.             if((*(WindowPeek)(**theCtl).contrlOwner).windowKind == dialogKind)
  169.                 (**hCDEF).itemNum  = HiWord((**theCtl).contrlRfCon);
  170.             if((**hCDEF).itemNum < 0 || (**hCDEF).itemNum > 100)                    
  171.                 (**hCDEF).itemNum = 0;
  172.         }
  173.         (**hCDEF).bumpit    = -3;
  174.                 
  175.         (**hCDEF).offPort     = 0;                    // offPort & 
  176.         (**hCDEF).pDepth     = 0;                    // pDepth filled in in doDraw()
  177.         (**hCDEF).qdVers     = getQDVers();
  178.         
  179.         (**hCDEF).poly[0] = 0;                        // filled in in makePolys()
  180.         (**hCDEF).poly[1] = 0;
  181.         (**hCDEF).poly[2] = 0;
  182.         (**theCtl).contrlData = (Handle)hCDEF;
  183.         makePolys(theCtl, varCode);                // draw arrows & frame
  184.         
  185.         HUnlock((Handle)hCDEF);
  186.     }
  187.     else
  188.         (**theCtl).contrlData = 0;
  189. }
  190.         
  191. //==================================================================================
  192. //    dispose of our private data (offscreen bitmap)
  193. //==================================================================================
  194.  
  195. static void doDisp (ControlHandle theCtl)
  196. {
  197.     hSpinData    hCDEF;
  198.     BitMap        *theBits;
  199.     short        inx;
  200.     
  201.     hCDEF = (hSpinData)(**theCtl).contrlData;
  202.     if(hCDEF) {
  203.         if((**hCDEF).offPort) {
  204.             if(((**hCDEF).offPort->portVersion & 0x8000) != 0) {    // offPort is GWorld
  205.                 DisposeGWorld((**hCDEF).offPort);                
  206.             }
  207.             else {                                                    // offport is bitmap
  208.                 theBits = &((GrafPtr)(**hCDEF).offPort)->portBits;
  209.                 DisposePtr((*theBits).baseAddr);
  210.                 ClosePort((GrafPtr)(**hCDEF).offPort);
  211.                 DisposePtr((Ptr)(**hCDEF).offPort);
  212.             }
  213.         }
  214.         for(inx=0;inx<3;inx++) {
  215.             if((**hCDEF).poly[inx])
  216.                 KillPoly((**hCDEF).poly[inx]);
  217.         }
  218.         HUnlock((Handle)hCDEF);
  219.         DisposeHandle((Handle)hCDEF);
  220.         (**theCtl).contrlData = 0;
  221.     }
  222. }
  223.  
  224. //==================================================================================
  225. //    create polygons for the VERTICAL arrow frame and the up and down arrows    
  226. //==================================================================================
  227.  
  228. static void makePolys(ControlHandle theCtl, short varCode)
  229. {
  230.     short        c,v,w,b1,b2;    
  231.     Rect        r,parts[NUMPARTS];
  232.     hSpinData    hCDEF;
  233.  
  234.     hCDEF = (hSpinData)(**theCtl).contrlData;
  235.     if(!hCDEF) {                                        // ooops!                    
  236.         return;
  237.     }
  238.     getRects(theCtl, varCode, parts);                    // rects for control parts
  239.     OffsetRect(&parts[FRAME],                             // adjust to 0,0            
  240.                 -parts[ALL].left, -parts[ALL].top);    
  241.     r = parts[FRAME];
  242.     
  243.     (**hCDEF).poly[0] = OpenPoly();                        // draw frame for arrows
  244.     
  245.     if((**hCDEF).poly[0]) {
  246.         MoveTo(r.left+2,r.top);    
  247.         LineTo(r.right-3,r.top);
  248.         Line(2,3);
  249.         LineTo(r.right-1,r.bottom-3);
  250.         Line(-2,2);
  251.         LineTo(r.left+2,r.bottom-1);
  252.         Line(-2,-2);
  253.         LineTo(r.left,r.top+3);
  254.         Line(2,-3);
  255.         ClosePoly(); // poly[0]
  256.     }
  257.     
  258.     if(varCode & horizArrows) {
  259.         v = (r.bottom - r.top)/2 + r.top;
  260.         b1 = (r.bottom - r.top -2)/3;
  261.         b2 = (r.bottom - r.top -2)/4;
  262.         c = r.right -3;
  263.         w = b2;
  264.         if(varCode & bigArrows) {
  265.             c--;
  266.             w++;
  267.         }
  268.     }
  269.     else {
  270.         c = (r.right - r.left)/2 + r.left;
  271.         b1 = (r.right - r.left -2)/3;
  272.         b2 = (r.right - r.left -2)/4;
  273.         v = r.top+2;
  274.         w = b2;
  275.         if(varCode & bigArrows) {
  276.             v++;
  277.             w++;
  278.         }
  279.     }
  280.     (**hCDEF).poly[1] = OpenPoly();                        // draw UP arrow
  281.     
  282.     if((**hCDEF).poly[1]) {
  283.         if(varCode & horizArrows) {
  284.             MoveTo(c,v);                                // apex of arrow
  285.             Line(-b1,-b1);                                // upper slope of arrow head
  286.             Line(0,2);                                    // down to shaft
  287.             Line(-b2,0);                                // left to end
  288.             Line(0,w);                                    // down for shaft end
  289.             Line(b2,0);                                    // botttom edge of shaft
  290.             Line(0,2);                                    // bottom of arrow
  291.             Line(b1,-b1);                                // lower slope of arrow head
  292.         }
  293.         else {
  294.             MoveTo(c,v);                                // see above
  295.             Line(-b1,b1);
  296.             Line(2,0);
  297.             Line(0,b2);
  298.             Line(w,0);
  299.             Line(0,-b2);
  300.             Line(2,0);
  301.             Line(-b1,-b1);
  302.         }
  303.         ClosePoly(); // poly[1]
  304.     }
  305.     
  306.     (**hCDEF).poly[2] = OpenPoly();                        // draw LEFT arrow
  307.     
  308.     if((**hCDEF).poly[2]) {        
  309.         if(varCode & horizArrows) {    
  310.             c = r.left+2;
  311.             if(varCode & bigArrows)
  312.                 c++;
  313.             
  314.             MoveTo(c,v);                                // see above
  315.             Line(b1,-b1);
  316.             Line(0,2);
  317.             Line(b2,0);
  318.             Line(0,w);
  319.             Line(-b2,0);
  320.             Line(0,2);
  321.             Line(-b1,-b1);
  322.         }
  323.         else {                                            // draw DOWN arrow
  324.             v = r.bottom-3;
  325.             if(varCode & bigArrows)
  326.                 v--;
  327.                 
  328.             MoveTo(c,v);                                // see above
  329.             Line(-b1,-b1);
  330.             Line(2,0);
  331.             Line(0,-b2);
  332.             Line(w,0);
  333.             Line(0,b2);
  334.             Line(2,0);
  335.             Line(-b1,b1);
  336.         }
  337.         ClosePoly(); // poly[2]
  338.     }
  339. }
  340.  
  341. //==================================================================================
  342. //    check for a mouseDown in our control. Actually, we do all of our 
  343. //    tracking, drawing and updating of the control here.    
  344. //==================================================================================
  345.  
  346. static long doTest (ControlHandle theCtl, short varCode, long param)
  347. {
  348.     Point        p;
  349.     short        inx,partCode;
  350.     long        ret = 0;
  351.     Rect        parts[NUMPARTS];
  352.     hSpinData    hCDEF;
  353.     
  354.     hCDEF = (hSpinData)(**theCtl).contrlData;
  355.     if(!hCDEF)
  356.         return(ret);
  357.     
  358.     p.h = LoWord(param);                            // get the mouse hit point        
  359.     p.v = HiWord(param);        
  360.     
  361.     getRects(theCtl, varCode, parts);
  362.     
  363.     partCode = (**theCtl).contrlHilite;
  364.     
  365.     
  366.     for(inx = UPARROW;inx <= DOWNARROW;inx++) {        
  367.         if(PtInRect(p,&parts[inx])) {
  368.             if(inx == partCode) {
  369.                 updateValue(theCtl, inx);            // change value
  370.                 doDraw(theCtl, varCode);            // draw it
  371.                 accelerate(theCtl);                    // speed up if needed
  372.             }
  373.             else {                                    // reset increment
  374.                 (**hCDEF).currIncr = (**hCDEF).increment;    
  375.                 (**hCDEF).bumpit = -3;
  376.             }
  377.             ret = inx;
  378.         }
  379.     }
  380.     return(ret);    
  381. }
  382.  
  383. //==================================================================================
  384. //    update the controlValue and pause a bit
  385. //==================================================================================
  386.  
  387. static void updateValue (ControlHandle theCtl, short partCode)
  388. {
  389.     short        t,incr1,incr2,incr3;
  390.     Handle        hEdit;
  391.     Rect        r;
  392.     long        secs;
  393.     Str31        s;
  394.     hSpinData    hCDEF;
  395.     
  396.     hCDEF = (hSpinData)(**theCtl).contrlData;
  397.     if(!hCDEF)
  398.         return;
  399.             
  400. //----------------------------------------------------------------------------------
  401. // Update the controValue
  402. //----------------------------------------------------------------------------------
  403.     if(partCode == UPARROW) {
  404.         if((**theCtl).contrlValue <= (**theCtl).contrlMax - (**hCDEF).currIncr) 
  405.             (**theCtl).contrlValue += (**hCDEF).currIncr;
  406.         else
  407.             (**theCtl).contrlValue = (**theCtl).contrlMax;
  408.     }
  409.     else {
  410.         if((**theCtl).contrlValue >= (**theCtl).contrlMin + (**hCDEF).currIncr)
  411.             (**theCtl).contrlValue -= (**hCDEF).currIncr;
  412.         else
  413.             (**theCtl).contrlValue = (**theCtl).contrlMin;
  414.     }
  415.     
  416. //----------------------------------------------------------------------------------
  417. // if we got an Edit Text item number in the RefCon, put the text
  418. //    into the edit item        
  419. //----------------------------------------------------------------------------------                            
  420.     
  421.     if((**hCDEF).itemNum) {
  422.         GetDItem((**theCtl).contrlOwner,         // get handle to text item    
  423.                     (**hCDEF).itemNum, &t, &hEdit, &r);
  424.         if(hEdit && (t & editText))    {                // is it valid ?
  425.             NumToString((long)(**theCtl).contrlValue, s);
  426.             SetIText(hEdit,s);
  427.             SelIText((**theCtl).contrlOwner, (**hCDEF).itemNum, 32767, 32767);
  428.         }
  429.         else
  430.             (**hCDEF).itemNum = 0;
  431.     }
  432.     
  433. //----------------------------------------------------------------------------------
  434. // now, pause a bit to let the user catch up
  435. //----------------------------------------------------------------------------------    
  436.  
  437.     incr1 = (**hCDEF).increment;
  438.     if(incr1 == 1)
  439.         incr2 = 10;
  440.     else
  441.         incr2 = incr1*incr1;
  442.     incr3 = incr2*incr2;
  443.     
  444.     if((**hCDEF).currIncr == incr1)
  445.         Delay(10L, &secs);
  446.     else
  447.     if((**hCDEF).currIncr == incr2)
  448.         Delay(12L, &secs);
  449.     else
  450.     if((**hCDEF).currIncr == incr3)
  451.         Delay(16L, &secs);
  452. }
  453.  
  454. //==================================================================================
  455. //    process the drawCntl message    
  456. //==================================================================================
  457.  
  458. void doDraw (ControlHandle theCtl, short varCode)
  459. {
  460.     RgnHandle        saveClip, newClip;
  461.     Boolean            inactive=false,inColor=false,bgInColor=false;
  462.     Boolean            haveGW=false,haveGray=false,drawText=true;
  463.     PenState        penSt;
  464.     short            partCode=0,val,wid,h,v;
  465.     Str31            s;
  466.     Rect            parts[NUMPARTS];
  467.     BitMap            *offBits;
  468.     PixMapHandle    pmHdl;
  469.     CGrafPtr        savePort;
  470.     GDHandle        saveGDev;
  471.     RGBColor        saveFore,saveBack;
  472.     RGBColor        rgbBlack = {0,0,0};
  473.     RGBColor        rgbWhite = {-1,-1,-1};
  474.     hSpinData        hCDEF;
  475.  
  476.     hCDEF = (hSpinData)(**theCtl).contrlData;
  477.     if(!hCDEF)                                                // ooops!                    
  478.         return;
  479.  
  480. //----------------------------------------------------------------------------------        
  481. // Get drawing rects, offPort and lock down the pixels
  482. //----------------------------------------------------------------------------------    
  483.  
  484.     getRects(theCtl, varCode, parts);                        // rects for control parts
  485.     
  486.     (**hCDEF).pDepth = getOff(&(**hCDEF).offPort, &parts[ALL]);
  487.         
  488.     if((**hCDEF).pDepth == 0)                                // oh… oh…                    
  489.         return;                                        
  490.  
  491.     if(((**hCDEF).offPort->portVersion & 0x8000) != 0) {    // have a GWorld
  492.         haveGW = true;
  493.         pmHdl = getLockedPixels(&(**hCDEF).offPort, (**hCDEF).qdVers);
  494.         if(!pmHdl)
  495.             return;                                            // nuts…
  496.     }
  497.         
  498. //----------------------------------------------------------------------------------    
  499. // initialize our drawing stuff    
  500. //----------------------------------------------------------------------------------    
  501.     
  502.     if((**theCtl).contrlHilite == 0xFF)
  503.         inactive = true;
  504.     else
  505.         partCode = (**theCtl).contrlHilite;
  506.         
  507.     GetPenState(&penSt);    
  508.     
  509.     if ((**hCDEF).pDepth > 2) {
  510.         inColor = true;
  511.         saveColors(&saveFore, &saveBack);
  512.         bgInColor = true;
  513.         if(saveBack.red == 65535 &&                        // is bg white?
  514.             saveBack.green == 65535 &&
  515.             saveBack.blue == 65535)
  516.             bgInColor = false;
  517.     }
  518. #ifdef NOEMBOSS
  519.     bgInColor = false;
  520. #endif    
  521.  
  522. //----------------------------------------------------------------------------------
  523. //    Do the clip region properly.  Thanks Ari!
  524. //----------------------------------------------------------------------------------
  525.  
  526.     saveClip = NewRgn();
  527.     GetClip(saveClip);
  528.     
  529.     newClip = NewRgn();
  530.     RectRgn(newClip, &parts[ALL]);
  531.     SectRgn(saveClip, newClip, newClip);
  532.     
  533.     if(EmptyRgn(newClip)) {                                    // if empty, don't waste
  534.         DisposeRgn(saveClip);                                // time drawing...
  535.         DisposeRgn(newClip);
  536.         return;
  537.     }
  538.  
  539.     SetClip(newClip);
  540.  
  541. //----------------------------------------------------------------------------------    
  542. // set our offScreen port so we can draw to it    
  543. //----------------------------------------------------------------------------------
  544.  
  545.     if(haveGW) {                                        // we have a pixMap
  546.         GetGWorld(&savePort, &saveGDev);
  547.         SetGWorld((**hCDEF).offPort, nil);
  548.         offBits = (BitMap *)*pmHdl;
  549.         if((**(*savePort).bkPixPat).patType != 0 &&
  550.             (savePort->portVersion & 0x8000) != 0)
  551.             BackPixPat((*savePort).bkPixPat);
  552.     }
  553.     else {                                                // we have a bitMap    
  554.         GetPort((GrafPtr*)&savePort);
  555.         SetPort((GrafPtr)(**hCDEF).offPort);        
  556.         offBits = (BitMap *)&((GrafPtr)(**hCDEF).offPort)->portBits;
  557.     }
  558.  
  559.     if(inColor) {                                        // set colors in offPort    
  560.         RGBForeColor(&saveFore);
  561.         RGBBackColor(&saveBack);
  562.     }
  563.     else {
  564.         ForeColor(blackColor);
  565.         BackColor(whiteColor);
  566.     }
  567.     
  568.     if(varCode & useWindFont) {                            // set font in offPort    
  569.         TextFont(savePort->txFont);
  570.         TextSize(savePort->txSize);
  571.     }
  572.         
  573. //----------------------------------------------------------------------------------
  574. // Finally, we are ready to start drawing... first, erase the background.
  575. //----------------------------------------------------------------------------------
  576.     
  577.     SetOrigin((**theCtl).contrlRect.left, (**theCtl).contrlRect.top);
  578.     EraseRect(&offBits->bounds);
  579.     SetOrigin(0, 0);
  580.     
  581. //----------------------------------------------------------------------------------
  582. // draw control title into offPort
  583. //----------------------------------------------------------------------------------
  584.  
  585.     wid = parts[ALL].right - parts[ALL].left;
  586.     if(varCode & bigArrows && wid == ARROWID1)
  587.         drawText = false;
  588.     else
  589.     if(wid <= ARROWID+15)
  590.         drawText = false;
  591.             
  592.     if(drawText) {
  593.         OffsetRect(&parts[TITLE],                             // adjust to 0,0            
  594.                     -parts[ALL].left, -parts[ALL].top);
  595.         h = parts[TITLE].left +1;
  596.         v = parts[TITLE].bottom -parts[BASELINE].bottom;
  597.         
  598.         if(bgInColor && varCode & ctl3D) {                    // emboss the title
  599.             RGBForeColor(&rgbWhite);
  600.             MoveTo(h+1, v+1);
  601.             DrawString((**theCtl).contrlTitle);
  602.         }
  603.         if(inColor) {
  604.             setPartColor(theCtl, cTextColor, true);
  605.             if(inactive) {                                    // draw in gray    
  606.                 if(haveGrayText()) {
  607.                     TextMode(grayishTextOr);
  608.                     haveGray = true;
  609.                 }
  610.             }
  611.         }
  612.         MoveTo(h, v);
  613.         DrawString((**theCtl).contrlTitle);
  614.         TextMode(srcOr);
  615.     
  616. //----------------------------------------------------------------------------------
  617. // draw control value into offPort after converting to a string 
  618. //----------------------------------------------------------------------------------
  619.  
  620.         val = (**theCtl).contrlValue;
  621.         NumToString((long)val, s);
  622.         wid = StringWidth(s) + 6;
  623.         h = parts[TITLE].right - wid;
  624.         
  625.         if(bgInColor && varCode & ctl3D) {                    // emboss the value
  626.             RGBForeColor(&rgbWhite);
  627.             MoveTo(h+1, v+1);
  628.             DrawString(s);
  629.         }
  630.         if(inColor) {
  631.             setPartColor(theCtl, cTextColor, true);
  632.             if(haveGray)                                    // draw in gray
  633.                 TextMode(grayishTextOr);
  634.         }
  635.         MoveTo(h, v);
  636.         DrawString(s);
  637.         TextMode(srcOr);
  638.     }
  639.     else
  640.     if(inactive && inColor)
  641.         haveGray = haveGrayText();
  642.     
  643. //----------------------------------------------------------------------------------
  644. // draw the arrows into the offPort    
  645. //----------------------------------------------------------------------------------
  646.             
  647.     drawArrows(theCtl, varCode, partCode, inactive, inColor);
  648.     
  649. //----------------------------------------------------------------------------------
  650. // now blit the image from offscreen port to onscreen 
  651. //----------------------------------------------------------------------------------
  652.  
  653.     if(haveGW)                                            // back to onscreen port    
  654.         SetGWorld(savePort, saveGDev);
  655.     else
  656.         SetPort((GrafPtr)savePort);
  657.  
  658.     if(inColor) {
  659.         RGBForeColor(&rgbBlack);
  660.         RGBBackColor(&rgbWhite);
  661.     }
  662.     
  663.     CopyBits(offBits,                                    // from bitmap                
  664.             &((GrafPtr)savePort)->portBits,                 // to onscreen port            
  665.             &(**hCDEF).offPort->portRect,                // from rect                
  666.             &parts[ALL],                                 // to rect                    
  667.             srcCopy, nil);
  668.     
  669.     if(haveGW) {
  670.         unlockPixels(pmHdl, (**hCDEF).qdVers);
  671.         DisposeGWorld((**hCDEF).offPort);
  672.         (**hCDEF).offPort = 0;
  673.     }
  674.         
  675.     if(inColor)
  676.         restoreColors(&saveFore, &saveBack);
  677.     
  678. //----------------------------------------------------------------------------------
  679. // if inactive & System 6.0.x, gray out the control the old way    
  680. //----------------------------------------------------------------------------------
  681.  
  682.     if(inactive && !haveGray) {    
  683.         PenPat( (ConstPatternParam) "\xAA\x55\xAA\x55\xAA\x55\xAA\x55");
  684.         PenMode(patBic);
  685.         PaintRect(&parts[ALL]);        
  686.     }
  687.     SetClip(saveClip);
  688.     DisposeRgn(saveClip);
  689.     DisposeRgn(newClip);
  690.  
  691.     SetPenState(&penSt);
  692. }
  693.  
  694. //==================================================================================
  695. //    Draw the arrow control image.
  696. //==================================================================================
  697.  
  698. static void drawArrows(ControlHandle theCtl, short varCode, short partCode, 
  699.                         Boolean inactive, Boolean inColor)
  700. {
  701.     short        inx;
  702.     Rect        arrowRect,parts[NUMPARTS];
  703.     RGBColor    rgbBlack = {0,0,0};
  704.     RGBColor    rgbWhite = {-1,-1,-1};
  705.     RGBColor    rgbA = {0xAAAA, 0xAAAA, 0xAAAA};
  706.     RGBColor    grayColor;
  707.     Boolean        gotGray=false, canHilite[2];
  708.     hSpinData    hCDEF;
  709.     
  710.     hCDEF = (hSpinData)(**theCtl).contrlData;
  711.     
  712.     getRects(theCtl, varCode, parts);            // rects for control parts
  713.  
  714.     if((**theCtl).contrlValue == (**theCtl).contrlMax)
  715.         canHilite[0] = false;
  716.     else
  717.         canHilite[0] = true;
  718.  
  719.     if((**theCtl).contrlValue == (**theCtl).contrlMin)
  720.         canHilite[1] = false;
  721.     else
  722.         canHilite[1] = true;
  723.  
  724.         
  725. //----------------------------------------------------------------------------------
  726. // Draw a frame around the up and down arrows (poly[0] is the frame).
  727. //----------------------------------------------------------------------------------
  728.  
  729.     if((**hCDEF).poly[0]) {                        // this poly is the frame for both
  730.         ForeColor(whiteColor);                    // arrows
  731.         PaintPoly((**hCDEF).poly[0]);
  732.         ForeColor(blackColor);
  733.         if(inColor) {
  734.             gotGray = getGray(&grayColor);
  735.             setPartColor(theCtl, cFrameColor, true);
  736.             if(inactive && gotGray)
  737.                 RGBForeColor(&grayColor);
  738.         }
  739.         FramePoly((**hCDEF).poly[0]);
  740.         
  741. //----------------------------------------------------------------------------------
  742. // draw shadow around frame if 3D arrows were requested
  743. //----------------------------------------------------------------------------------
  744.  
  745.         if(inColor && varCode & ctl3D && !inactive) {
  746.             arrowRect = parts[FRAME];
  747.             OffsetRect(&arrowRect, -parts[ALL].left, -parts[ALL].top);
  748.             RGBForeColor(&rgbA);
  749.             
  750.             MoveTo(arrowRect.left+1, arrowRect.bottom-1);
  751.             LineTo(arrowRect.left-1, arrowRect.bottom-3);
  752.             LineTo(arrowRect.left-1, arrowRect.top+3);
  753.             Move(0,-1);
  754.             LineTo(arrowRect.left+2, arrowRect.top-1);
  755.             LineTo(arrowRect.right-4, arrowRect.top-1);
  756.             
  757.             ForeColor(whiteColor);
  758.             Move(1,0);
  759.             LineTo(arrowRect.right, arrowRect.top+2);
  760.             LineTo(arrowRect.right, arrowRect.bottom-3);
  761.             LineTo(arrowRect.right-3, arrowRect.bottom);
  762.             LineTo(arrowRect.left+2, arrowRect.bottom);            
  763.  
  764.             setPartColor(theCtl, cFrameColor, true);
  765.         }
  766.     }
  767.     
  768. //----------------------------------------------------------------------------------
  769. // If a small b&w arrow was clicked, fill half the frame with black (fg color, 
  770. // leaving frame untouched).  Then we fill/paint the arrow poly in white.
  771. //----------------------------------------------------------------------------------
  772.  
  773.     if(!(varCode & ctl3D) || !inColor) {
  774.         if(partCode == UPARROW || partCode == DOWNARROW) {
  775.             if(canHilite[partCode-2] && !(varCode & bigArrows)) {
  776.  
  777.                 arrowRect = parts[partCode];
  778.                 OffsetRect(&arrowRect, -parts[ALL].left, -parts[ALL].top);
  779.                 
  780.                 if(varCode & horizArrows) {
  781.                     if(partCode == UPARROW)
  782.                         MoveTo(arrowRect.left, arrowRect.top+1);
  783.                     else
  784.                         MoveTo(arrowRect.left+1,arrowRect.top+1);
  785.                     for(inx=0;inx<8;inx++) {
  786.                         Line(0,8);
  787.                         Move(1,-8);
  788.                     }
  789.                 }
  790.                 else {
  791.                     if(partCode == UPARROW)
  792.                         MoveTo(arrowRect.left+1, arrowRect.top+1);
  793.                     else
  794.                         MoveTo(arrowRect.left+1,arrowRect.top-1);
  795.                     for(inx=0;inx<8;inx++) {
  796.                         Line(8,0);
  797.                         Move(-8,1);
  798.                     }
  799.                 }
  800.             }
  801.         }
  802.     }
  803.  
  804. //----------------------------------------------------------------------------------
  805. // Draw both the top & bottom arrows (poly[1] & poly[2])
  806. //----------------------------------------------------------------------------------
  807.  
  808.     for(inx=1;inx<=2;inx++) {
  809.         if((**hCDEF).poly[inx]) {                            // Need the poly!
  810.         
  811. //----------------------------------------------------------------------------------
  812. //     First check to see if we are drawing a hilited arrow - and fill the center
  813. //    appropriately.
  814. //----------------------------------------------------------------------------------
  815.  
  816.             if(partCode == inx+1 && canHilite[inx-1]) {        // arrow is hilited
  817.                 if(varCode & bigArrows) {
  818.                     if(inColor)
  819.                         RGBForeColor(&rgbBlack);            // always black
  820.                 }
  821.                 else {                                        // small arrow
  822.                     if(inColor) {
  823.                         if(varCode & ctl3D)                    // drawn in black
  824.                             RGBForeColor(&rgbBlack);        // for 3D
  825.                         else
  826.                             RGBForeColor(&rgbWhite);        // white for non-3D    
  827.                     }
  828.                     else                                    // b&w hilited arrow
  829.                         ForeColor(whiteColor);                // must draw white
  830.                 }
  831.                 PaintPoly((**hCDEF).poly[inx]);                // fill the arrow
  832.             }
  833.             
  834. //----------------------------------------------------------------------------------
  835. // Nope, it is not hilited
  836. //----------------------------------------------------------------------------------
  837.  
  838.             else {
  839.                 if(!canHilite[inx-1]) {
  840.                     if(inColor)
  841.                         RGBForeColor(&grayColor);
  842.                 }
  843.                 if(varCode & ctl3D) {                        // make it look like a
  844.                     if(inColor) {                            // scroll bar arrow
  845.                         if(!inactive && canHilite[inx-1]) {    // with lighter center
  846.                             setPartColor(theCtl, cTingeLight, true);
  847.                             GetForeColor(&grayColor);
  848.                             grayColor.red -= 13107;
  849.                             grayColor.green -= 13107;
  850.                             grayColor.blue -= 13107;
  851.                             RGBForeColor(&grayColor);
  852.                             PaintPoly((**hCDEF).poly[inx]);    // filled color is light
  853.                             ForeColor(blackColor);            // needed for sys6
  854.                             setPartColor(theCtl, cTingeDark, true);
  855.                         }
  856.                     }
  857.                     else
  858.                     if(!(varCode & bigArrows))                // 3D, but in b&w, small
  859.                         PaintPoly((**hCDEF).poly[inx]);        // arrow is solid fgColor
  860.                 }
  861.                 else                                        // not 3D
  862.                 if(!(varCode & bigArrows))                    // so, small arrow
  863.                     PaintPoly((**hCDEF).poly[inx]);            // is solid fgColor
  864.             }
  865.                 
  866. //----------------------------------------------------------------------------------
  867. //    Finally, frame the arrow
  868. //----------------------------------------------------------------------------------
  869.  
  870.             FramePoly((**hCDEF).poly[inx]);                    // frame the arrow
  871.             
  872.             if(!inactive) {                                    // set fg for next arrow
  873.                 if(inColor)
  874.                     setPartColor(theCtl, cFrameColor, true);
  875.                 else
  876.                     ForeColor(blackColor);
  877.             }
  878.         }
  879.     }
  880. }
  881.  
  882. //==================================================================================
  883. //    Get the rectangles for each part of the control.  These are used for
  884. //    detecting mouse actions and drawing the control.
  885. //==================================================================================
  886.  
  887. static void getRects    (ControlHandle theCtl, short varCode, Rect parts[])
  888. {
  889.     short        aHt,aWid,fHt,cHt,offset;
  890.     Rect         base,work,rUp,rDown;
  891.     FontInfo    f;
  892.     
  893. //----------------------------------------------------------------------------------
  894. // set sizes for arrow height & width
  895. //----------------------------------------------------------------------------------    
  896.     if(varCode & bigArrows) {
  897.         if(varCode & horizArrows) {
  898.             aHt = ARROWID1;
  899.             aWid = ARROWHT1;
  900.         }
  901.         else {
  902.             aHt = ARROWHT1;
  903.             aWid = ARROWID1;
  904.         }
  905.     }
  906.     else {
  907.         if(varCode & horizArrows) {
  908.             aHt = ARROWID;
  909.             aWid = ARROWHT;
  910.         }
  911.         else {
  912.             aHt = ARROWHT;
  913.             aWid = ARROWID;
  914.         }
  915.     }
  916.     
  917. //----------------------------------------------------------------------------------
  918. // part rectangles for Arrow portions of control, right justified in control rect
  919. //----------------------------------------------------------------------------------    
  920.     base = (**theCtl).contrlRect;                        // the entire control
  921.     cHt = base.bottom-base.top;                            // control height    
  922.     parts[ALL] = parts[BASELINE] = base;    
  923.         
  924.     base.top = base.top + (cHt - aHt)/2;                // center arrow in rect
  925.     base.bottom = base.top + aHt;
  926.     
  927.     rUp = work = base;
  928.     OffsetRect(&rUp,-1,0);            
  929.     rUp.left = rUp.right - aWid;                        // shift arrow to right        
  930.     parts[FRAME] = rDown = rUp;                            // rUp & rDown are full rects
  931.     
  932.     if(varCode & horizArrows)    
  933.         rUp.left = rUp.right - (rUp.right - rUp.left)/2;// split them in half        
  934.     else
  935.         rUp.bottom = rUp.top + (rUp.bottom - rUp.top)/2;            
  936.     parts[UPARROW] = rUp;                                // rUp is top/right half
  937.         
  938.     if(varCode & horizArrows)    
  939.         rDown.right = rUp.left-1;
  940.     else    
  941.         rDown.top = rUp.bottom+1;
  942.     parts[DOWNARROW] = rDown;                            // rDown is bottom/left half    
  943.     
  944. //----------------------------------------------------------------------------------
  945. // set text drawing from font metrics
  946. //----------------------------------------------------------------------------------    
  947.     GetFontInfo(&f);
  948.     fHt = f.ascent + f.descent+f.leading;
  949.     if(f.leading == 0) 
  950.         fHt++;
  951.     
  952.     offset = (aHt - fHt)/2;                                // center digits in rect    
  953.     work.bottom -= offset;
  954.     work.right -= aWid;
  955.     parts[TITLE] = work;
  956.     parts[BASELINE].bottom = fHt - f.ascent;            // baseline for DrawStr    
  957. }
  958.  
  959. //==================================================================================
  960. //    Accelerate the control.  If we roll over a tens or hundreds FIVE times
  961. //    and the mouse is still down, bump the increment. This lets the control
  962. //    spin a number up by 1's from 0-50, by 10's from 50-100, then by 100's.
  963. //==================================================================================
  964.  
  965. static void accelerate(ControlHandle theCtl)
  966. {
  967.     long        mult;
  968.     short        incr2;
  969.     hSpinData    hCDEF;
  970.     
  971.     hCDEF = (hSpinData)(**theCtl).contrlData;
  972.     if(!hCDEF)
  973.         return;
  974.         
  975.     if((**hCDEF).increment == 1)
  976.         incr2 = 10;
  977.     else
  978.         incr2 = (**hCDEF).increment*(**hCDEF).increment;
  979.         
  980.     mult = (**theCtl).contrlValue % incr2;
  981.     if((**hCDEF).currIncr == 
  982.         (**hCDEF).increment && !mult) {            // just rolled over a ten        
  983.         if((**hCDEF).bumpit == 1) {                // for the fifth time            
  984.             (**hCDEF).currIncr = incr2;            // bump our increment value        
  985.             (**hCDEF).bumpit = -3;
  986.         }
  987.         else {
  988.             (**hCDEF).bumpit += 1;                // for the first time            
  989.         }
  990.     }
  991.     else
  992.     if((**hCDEF).currIncr == incr2 && !mult) {    // just rolled over a hundred    
  993.         if((**hCDEF).bumpit == 1) {                // for the fifth time            
  994.             (**hCDEF).currIncr = incr2*incr2;    // bump our increment value        
  995.         }
  996.         else {
  997.             (**hCDEF).bumpit += 1;                // for the first time            
  998.         }
  999.     }
  1000. }