home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK2.toast / Development Kits (Disc 2) / QuickTime / Programming Stuff / Documentation / develop articles / develop Issue 12 / Time Bases / TimeBaseSimple / DialControl / DialControl.c next >
Encoding:
C/C++ Source or Header  |  1997-02-26  |  17.2 KB  |  467 lines  |  [TEXT/MPS ]

  1. /* DialControl */
  2. /* A sample Control Definition */
  3. /* DialControl is a control designed to simulate those tape control */
  4. /* wheels on fancy video tape decks. */
  5. /* You spin the control as you spin the real thing, click in the  */
  6. /* indentation (called Ball here) and rotate it around. */
  7. /* As with the real thing, this control can rotate around quite a few revolutions. */
  8.  
  9. /* This sample shows how to do a few interesting things; */
  10. /* Track a control around a circular (well, sort of) path. */
  11. /* Communicate with the 'host' application if it wants to be communicated with, */
  12. /* during the tracking action (something our scroll bar does _not_ do). */
  13.  
  14. /* How to use it... */
  15. /* If you'd like to use this control in something other than the sample it's included with, here */
  16. /* are some hints. */
  17. /* When you create the control, the initialization routine _forces_ the control rectangle */
  18. /* to be square, so a real circle is drawn. */
  19. /* To get called every time the control value changes during TrackControl, put a  */
  20. /* ProcPtr in the control refCon field.... */
  21. /* void MyTrackProc(void) */
  22. /* and that will get called every time the control changes value.  This will be handy if you */
  23. /* want to update some other area of the screen when the control moves. */
  24. /* ••• WARNING!  Either put a proc pointer in the refCon, or ZERO IT! */
  25. /* This def will jump through the refCon if it is non-zero, period.  If you put a number in there */
  26. /* for some reason or other, I bet this thing will crash. */
  27.  
  28. /* part code returned for the ball is 2, the rest of the control (not ball) is 1 */
  29.  
  30. /* When you call TrackControl, pass (ProcPtr)-1 as the last TrackControl parameter, that will */
  31. /* tell the control manager to use the 'built-in' tracking routine, which is what you want. */
  32.  
  33. /* And that's about it.  Enjoy it. */
  34. /* Oh heck, I forgot.... */
  35. /* Copyright © 1992, Apple Computer Inc. */
  36. /* C.K. Haun */
  37. /* Apple DTS, April 1992 */
  38.  
  39. /* FIles used */
  40. /* DialControl.c */
  41. /* DialControl.make */
  42.  
  43. #include <Types.h>
  44. #include <Quickdraw.h>
  45. #include <Controls.h>
  46. #include <Events.h>
  47. #include <Memory.h>
  48. #include <ToolUtils.h>
  49.  
  50. /* A small macro, since I'm grabbing the control rectangle all the time */
  51. #define mTheRect Rect theRect = (*cHandle)->contrlRect
  52. /* My prototypes for this control */
  53. void RectOnPoint(Point thePoint, short wide, short hi, Rect *theRect);
  54. short WhichWay(ControlHandle cHandle, Point new);
  55. void CalcBallNow(short whatValue, ControlHandle cHandle, Rect *theRect);
  56. void CalcRegions(ControlHandle cHandle, long theRegion, short which, long *returnlong);
  57. void DrawIt(ControlHandle cHandle, long thePart, long *returnlong);
  58. void TestIt(ControlHandle cHandle, long thePoint, long *returnlong);
  59. void TrackMe(ControlHandle cHandle, long param, long *returnlong);
  60.  
  61. enum  {
  62.     kPBlack = 1, kPDKGray = 4, kPGray = 23, kPLGray = 3
  63. };
  64. enum {kWholeControl = 0,kNotBall,kBall, kAllIndicators=129};
  65. enum {kPosition0=0,kPosition1,kPosition2,kPosition3,kPosition4,kPosition5,kPosition6,kPosition7,kPosition8,kPosition9,kPosition10,kPosition11,kPosition12,kPosition13,kPosition14,kPosition15};
  66.  
  67. pascal long DIAL(short varCode, ControlHandle cHandle, short message, long param)
  68. {
  69. #pragma unused (varCode)
  70.     mTheRect;
  71.     Rect *theBall;
  72.     long retVal = nil;
  73.     WindowPtr temp;
  74.     GetPort(&temp);
  75.     SetPort((*cHandle)->contrlOwner);
  76.     
  77.     switch (message) {
  78.         case drawCntl:
  79.             DrawIt(cHandle, param, &retVal);
  80.             break;
  81.         case testCntl:
  82.             TestIt(cHandle, param, &retVal);
  83.             break;
  84.         case calcCRgns:
  85.             CalcRegions(cHandle, param, message, &retVal);
  86.             break;
  87.         case initCntl:
  88.             /* store a -1 */
  89.             /* in the action proc, so our defProc will get called to track */
  90.             /* the control */
  91.             (*cHandle)->contrlAction = (ProcPtr)-1;
  92.             /* make sure rect passed is square */
  93.             /* since I calculated the indicator for a circular oval. */
  94.             /* if it ain't circular, I make no guarentees */
  95.             if ((theRect.bottom - theRect.top) != (theRect.right - theRect.left)) {
  96.                 theRect.right = theRect.left + (theRect.bottom - theRect.top);
  97.                 (*cHandle)->contrlRect = theRect;
  98.             }
  99.             /* create a rect that will be the right size */
  100.             /* for the 'thumb'  */
  101.             (*cHandle)->contrlData = NewHandleClear(sizeof(Rect));
  102.             theBall = (Rect *)*((*cHandle)->contrlData);
  103.             theBall->top = nil;
  104.             theBall->left = nil;
  105.             theBall->bottom = (theRect.bottom - theRect.top) / 4;
  106.             theBall->right = (theRect.bottom - theRect.top) / 4;
  107.             break;
  108.         case dispCntl:
  109.             /* I didn't create any handles, so I have nothing to  */
  110.             /* dispose of */
  111.             break;
  112.         case posCntl:
  113.             /* do normal positioning */
  114.             break;
  115.         case thumbCntl:
  116.             /* I don't have a thumb, so bye bye */
  117.             break;
  118.         case dragCntl:
  119.             /* do normal dragging */
  120.             break;
  121.         case autoTrack:
  122.             TrackMe(cHandle, param, &retVal);
  123.             break;
  124.         case calcCntlRgn:
  125.         case calcThumbRgn:
  126.             /* See the memory manager chapter of Inside Mac VI for an explanaition  */
  127.             /* of these two messages.  In short, they are 32bit clean calc messages */
  128.             CalcRegions(cHandle, param, message, &retVal);
  129.             break;
  130.             
  131.     }
  132.     SetPort(temp);
  133.     return(retVal);
  134.     
  135. }
  136. /* the port is set on every call to our proc actions by me, so we needn't  */
  137. /* fret about it */
  138.  
  139. /* CalcRegions allows me to tell the COntrol Manager what region my control takes */
  140. /* up in the window.  The COntrol Manager uses it for */
  141. /* dragging and the like. */
  142.  
  143. void CalcRegions(ControlHandle cHandle, long theRegion, short which, long *returnlong)
  144. {
  145. #pragma unused (returnlong)
  146.     mTheRect;
  147.     OpenRgn();
  148.     /* here I'm checking to see if this is the non-32bit clean version of the calc message. */
  149.     /* if it is, I must clear the hi byte of the region  */
  150.     if (which == calcCRgns)
  151.         theRegion = theRegion & 0xFFFFFF;
  152.     if (which == calcCRgns || which == calcCntlRgn) {
  153.         FrameOval(&theRect);
  154.         CloseRgn((RgnHandle)theRegion);
  155.     } else {
  156.         /* I would calc my thumb here, but I just don't have one */
  157.     }
  158. }
  159.  
  160. /* DrawIt.....draws the control! */
  161. /* And I only actually have two parts, the control itself and the indicator ball thing */
  162.  
  163.  
  164. void DrawIt(ControlHandle cHandle, long thePart, long *returnlong)
  165. {
  166.     mTheRect;
  167.     Rect theBall;
  168.     Pattern currentPat;
  169.     PenState theState;
  170.     GetPenState(&theState);                                 /* save pen information */
  171.  
  172.     /* get the rect the ball is occupying now */
  173.     CalcBallNow((*cHandle)->contrlValue, cHandle, &theBall);
  174.     switch (thePart) {
  175.         case kWholeControl:                                             /* draw the whole control */
  176.         case kAllIndicators:                                    /* when the thing moves */
  177.         case kNotBall:                                             /* same thing for part 1 */
  178.             /* o' course, I'm getting patterns out of the SYstem pattern */
  179.             /* list here because I _can't_ say */
  180.             /* PenPat(&qd.Gray); */
  181.             /* since the CDEF doesn't have quickdraw globals to reference. */
  182.             /* The application does, and I _could_ use them, but this */
  183.             /* is just as easy. */
  184.             GetIndPattern(currentPat, sysPatListID, kPBlack);
  185.             PenPat(¤tPat);
  186.             PenSize(1, 1);
  187.             FrameOval(&theRect);
  188.             InsetRect(&theRect, 1, 1);
  189.             GetIndPattern(currentPat, sysPatListID, kPDKGray);
  190.             PenPat(¤tPat);
  191.             PenSize(3, 3);
  192.             FrameOval(&theRect);
  193.             InsetRect(&theRect, 3, 3);
  194.             PenSize(1, 1);
  195.             GetIndPattern(currentPat, sysPatListID, kPBlack);
  196.             PenPat(¤tPat);
  197.             FrameOval(&theRect);
  198.             InsetRect(&theRect, 1, 1);
  199.             GetIndPattern(currentPat, sysPatListID, kPGray);
  200.             PenPat(¤tPat);
  201.             PaintOval(&theRect);
  202.             GetIndPattern(currentPat, sysPatListID, kPBlack);
  203.             PenPat(¤tPat);
  204.             PaintOval(&theBall);
  205.             /* may be that we got here with SetCtrlValue, call the refCon proc if any */
  206.             if ((thePart == kAllIndicators)  && ((*cHandle)->contrlRfCon) )
  207.               (ProcPtr)((*cHandle)->contrlRfCon)();
  208.             break;
  209.         case kBall:                                             /* draw the ball */
  210.             GetIndPattern(currentPat, sysPatListID, kPBlack);
  211.             PenPat(¤tPat);
  212.             PaintOval(&theBall);
  213.             break;
  214.     }
  215.     SetPenState(&theState);
  216.  
  217.  
  218. }
  219.  
  220.  
  221.  
  222. /* TestIt checks to see if the mouse is down inside the control. */
  223. /* if it is, then I tell the control manager where. */
  224. /* I have two parts.  You are either in the ball (part 2) or */
  225. /* in the rest of the control (part 1) */
  226. void TestIt(ControlHandle cHandle, long thePoint, long *returnlong)
  227. {
  228.     mTheRect;
  229.     Point ourPoint;
  230.     Rect ballRect;
  231.     RgnHandle theR = NewRgn();
  232.     *returnlong = nil;
  233.     CalcBallNow((*cHandle)->contrlValue, cHandle, &ballRect);
  234.     OpenRgn();
  235.     FrameOval(&ballRect);
  236.     CloseRgn(theR);
  237.     ourPoint.v = (thePoint >> 16) & 0xFFFF;
  238.     ourPoint.h = thePoint & 0xFFFF;
  239.     /* hit in ball??? */
  240.     if (PtInRgn(ourPoint, theR)) {
  241.         *returnlong = kBall;
  242.     } else {
  243.     /* hit in control at all?? */
  244.         OpenRgn();
  245.         FrameOval(&ballRect);
  246.         CloseRgn(theR);
  247.         if (PtInRgn(ourPoint, theR))
  248.             *returnlong = kNotBall;
  249.     }
  250.     DisposeRgn(theR);
  251. }
  252.  
  253. /* TrackMe is our tacking for the ball thing. */
  254. /* I want to auto-track (I put -1 in the action proc), so the Control Manager */
  255. /* is going to call me to  to handle all the control movement */
  256. void TrackMe(ControlHandle cHandle, long param, long *returnlong)
  257. {
  258.     mTheRect;
  259.     Rect ballRect;
  260.     Pattern currentPat;
  261.     Point nowPoint, oldPoint;
  262.     short oldVal;
  263.     RgnHandle theR = NewRgn();
  264.     RgnHandle theCont = NewRgn();
  265.     long aLong;
  266.     /* only track if the ball has been clicked on */
  267.     if (param == kBall) {
  268.         OpenRgn();
  269.         FrameOval(&theRect);
  270.         CloseRgn(theCont);
  271.         GetMouse(&nowPoint);
  272.         oldPoint = nowPoint;
  273.         while (StillDown()) {
  274.             GetMouse(&nowPoint);
  275.             CalcBallNow((*cHandle)->contrlValue, cHandle, &ballRect);
  276.             OpenRgn();
  277.             FrameOval(&ballRect);
  278.             CloseRgn(theR);
  279.                 if (PtInRgn(nowPoint, theCont) && !PtInRgn(nowPoint, theR)) {
  280.                 /* moved out of the ball it started in.  see where */
  281.                 /* save the current value of the control */
  282.                 oldVal = (*cHandle)->contrlValue;
  283.                 /* WhichWay returns an amount moved (in ball positions)  */
  284.                 (*cHandle)->contrlValue += WhichWay(cHandle, nowPoint);
  285.                 if ((*cHandle)->contrlValue < 0)
  286.                     (*cHandle)->contrlValue = 0;
  287.                 if ((*cHandle)->contrlValue > (*cHandle)->contrlMax)
  288.                     (*cHandle)->contrlValue = (*cHandle)->contrlMax;
  289.                 /* did it really change? */    
  290.                 if (oldVal != (*cHandle)->contrlValue) {
  291.                     /* Yes, redraw */
  292.                     GetIndPattern(currentPat, sysPatListID, kPGray);
  293.                     PenPat(¤tPat);
  294.                     PaintRgn(theR);
  295.                     CalcBallNow((*cHandle)->contrlValue, cHandle, &ballRect);
  296.                     GetIndPattern(currentPat, sysPatListID, kPBlack);
  297.                     PenPat(¤tPat);                    
  298.                     PaintOval(&ballRect);
  299.                     /* call the application action proc, if there is one */
  300.                     if ((*cHandle)->contrlRfCon) {
  301.                         (ProcPtr)((*cHandle)->contrlRfCon)();
  302.                         
  303.                     }
  304.                     oldPoint = nowPoint;
  305.                     
  306.                     
  307.                 }
  308.             }
  309.         }
  310.     }
  311.     DisposeRgn(theCont);
  312.     DisposeRgn(theR);
  313. }
  314.  
  315. void TrackingActionProc(void)
  316. {
  317.     Debugger();
  318. }
  319.  
  320. /* CalcBallNow sets up the oval rect for one of the 16 positions the  */
  321. /* ball can have in our control. */
  322. /* Now I'm sure I could have done this better using, like, Math or something, */
  323. /* but this works and is a lot faster than doing trig. */
  324.  
  325. void CalcBallNow(short whatValue, ControlHandle cHandle, Rect *theTRect)
  326. {
  327.     mTheRect;
  328.     /* set up my calculation bases */
  329.     Rect baseBall = *((Rect *)*((*cHandle)->contrlData));
  330.     short center = theRect.left + ((theRect.right -theRect.left) / 2);
  331.     short cWide = (theRect.right -theRect.left);
  332.     short tWide = baseBall.right -baseBall.left;
  333.     short radii = ((cWide / 2) -3) -(tWide / 2);
  334.     Point rectCenter;
  335.     Point targetP;
  336.     /* convert the control value to a position in a 16 position rotary indicator */
  337.     short realPos = whatValue -((whatValue / 16) * 16);
  338.     rectCenter.v = theRect.top + (cWide / 2);
  339.     rectCenter.h = theRect.left + (cWide / 2);
  340.     *theTRect = baseBall;
  341.     /* NOTE: Tweak these values at your Peril! */
  342.     switch (realPos) {
  343.         case kPosition0:     /* up */
  344.             targetP.v = rectCenter.v - radii + 3;
  345.             targetP.h = rectCenter.h;
  346.             break;
  347.             
  348.         case kPosition1:         
  349.             targetP.v = rectCenter.v - (radii - (radii / 5));
  350.             targetP.h = rectCenter.h + (radii - (radii / 2));
  351.             break;
  352.         case kPosition2:                                             
  353.             targetP.v = rectCenter.v - (radii - (radii / 3));
  354.             targetP.h = rectCenter.h + (radii - (radii / 3));
  355.             
  356.             break;
  357.         case kPosition3:                                             
  358.             targetP.v = rectCenter.v - (radii - (radii / 2));
  359.             targetP.h = rectCenter.h + (radii - (radii / 5));
  360.             
  361.             break;
  362.         case kPosition4:   /* right */
  363.             targetP.v = rectCenter.v;
  364.             targetP.h = rectCenter.h + radii - 3;
  365.             
  366.             break;
  367.         case kPosition5:
  368.             targetP.v = rectCenter.v + (radii - (radii / 2));
  369.             targetP.h = rectCenter.h + (radii - (radii / 5));
  370.             
  371.             
  372.             break;
  373.         case kPosition6:
  374.             targetP.v = rectCenter.v + (radii - (radii / 3));
  375.             targetP.h = rectCenter.h + (radii - (radii / 3));
  376.             
  377.             break;
  378.         case kPosition7:
  379.             targetP.v = rectCenter.v + (radii - (radii / 5));
  380.             targetP.h = rectCenter.h + (radii - (radii / 2));
  381.             
  382.             break;
  383.         case kPosition8:   /* down */
  384.             targetP.v = rectCenter.v + radii - 3;
  385.             targetP.h = rectCenter.h;
  386.             
  387.             break;
  388.         case kPosition9:
  389.             targetP.v = rectCenter.v + (radii - (radii / 5));
  390.             targetP.h = rectCenter.h - (radii - (radii / 2));
  391.             
  392.             break;
  393.         case kPosition10:
  394.             targetP.v = rectCenter.v + (radii - (radii / 3));
  395.             targetP.h = rectCenter.h - (radii - (radii / 3));
  396.             
  397.             break;
  398.         case kPosition11:
  399.             targetP.v = rectCenter.v + (radii - (radii / 2));
  400.             targetP.h = rectCenter.h - (radii - (radii / 5));
  401.             
  402.             break;
  403.         case kPosition12: /* left */            
  404.             targetP.h = rectCenter.h - radii + 3;
  405.             targetP.v = rectCenter.v;
  406.             
  407.             break;
  408.         case kPosition13:
  409.             targetP.v = rectCenter.v - (radii - (radii / 2));
  410.             targetP.h = rectCenter.h - (radii - (radii / 5));
  411.             
  412.             break;
  413.         case kPosition14:
  414.             targetP.v = rectCenter.v - (radii - (radii / 3));
  415.             targetP.h = rectCenter.h - (radii - (radii / 3));
  416.             
  417.             break;
  418.         case kPosition15:
  419.             targetP.v = rectCenter.v - (radii - (radii / 5));
  420.             targetP.h = rectCenter.h - (radii - (radii / 2));
  421.             
  422.             break;
  423.     }
  424.     /* make a rect */
  425.     RectOnPoint(targetP, tWide, tWide, theTRect);
  426.     
  427. }
  428.  
  429. /*  RectOnPoint is a little utility to center a rectanlge on a point */
  430. void RectOnPoint(Point thePoint, short wide, short hi, Rect *theRect)
  431. {
  432.     short hWide = wide / 2;
  433.     short vWide = hi / 2;
  434.     theRect->top = thePoint.v - vWide;
  435.     theRect->bottom = thePoint.v + vWide;
  436.     theRect->left = thePoint.h - hWide;
  437.     theRect->right = thePoint.h + hWide;
  438. }
  439.  
  440. /* WhichWay tells me which way the user is moving the ball, so I can  */
  441. /* update the control value and redraw the ball. */
  442. /* Since there may be a little latency (uuuuh, I may miss a movement) */
  443. /* I check ahead and behind 4 positions.  This '4' was determined empirically, */
  444. /* you may want to have a longer scan, or a shorter one. */
  445. short WhichWay(ControlHandle cHandle, Point new)
  446. {
  447.     short which = 0;
  448.     Rect theTestRect;
  449.     short testNum = 1;
  450.     /* walk forwards and backwards to see if the user has moved into a new */
  451.     /* ball position */
  452.     do {
  453.         CalcBallNow((*cHandle)->contrlValue - testNum, cHandle, &theTestRect);
  454.         if (PtInRect(new, &theTestRect)) {
  455.             which = testNum * -1;
  456.         } else {
  457.             CalcBallNow((*cHandle)->contrlValue + testNum, cHandle, &theTestRect);
  458.             if (PtInRect(new, &theTestRect))
  459.                 which = testNum;
  460.             
  461.         }
  462.         testNum++;
  463.     }while (testNum < 4);  /* don't go more than 4 positions away */
  464.     
  465.     return(which);
  466. }
  467.