home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Code Resources / Meter Control / Meter.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-05-06  |  13.8 KB  |  517 lines  |  [TEXT/ALFA]

  1. /**************************************************************************
  2. ** Meter.c
  3. **
  4. ** Implements an analog meter-style dial control.
  5. **
  6. **
  7. ** Brent Burton,   12/10/92
  8. ** brentb@math.tamu.edu
  9. **
  10. ***************************************************************************/
  11.  
  12. #include <MacHeaders>
  13. #include <Sane.h>
  14. #include "Meter.h"
  15.  
  16. /*********************************************************************/
  17.  
  18. /***********
  19. **  The following structure is used to store some information
  20. **  concerning the current state of the control - like where the
  21. **  last point was we drew, the last value, etc.  The value and
  22. **  position of the needle are recorded so that when we draw new
  23. **  values, we can use a fast XOR operation rather than clearing
  24. **  the area with EraseRect() or EraseOval() or whatever.  The
  25. **  other information about center and radius is updated whenever
  26. **  the control is to be redrawn - it might have moved or changed shape.
  27. ************/
  28.  
  29. typedef struct {
  30.     PicHandle    thePict;        /* picture for basic control shape */
  31.     Point        center;            /* Center of meter's rectangle     */
  32.     Point        radius;            /* radius of needle sweep area     */
  33.     Point        lastPt;            /* last point needle was drawn to. */
  34.     int            lastValue;        /* last value of control           */
  35. } CI, *CIP, **CIH;                /* Control Information record      */
  36.  
  37.  
  38. typedef struct {        /* Record passed via param during thumbCntl msg */
  39.     Rect limitRect, slopRect;
  40.     int        axis;
  41. } TR, *TRP;
  42.  
  43.  
  44. /**********************************************************************/
  45.  
  46. pascal long        main(int, ControlHandle, int, long);
  47. static void        Draw( ControlHandle, long);
  48. static long        Test( ControlHandle, long);
  49. static void        DragNeedle(ControlHandle);
  50.  
  51. static void        DrawNeedle(ControlHandle, Point, Point);
  52. static void        DrawValue(ControlHandle, int, int);
  53. static void        ResetPoints(ControlHandle);
  54. static void        MapValue2Pt(ControlHandle, Point *);
  55. static int        MapValue2Angle(ControlHandle );
  56. static int         MapAngle2Value(ControlHandle , int );
  57. static void        MapAngle2Pt(ControlHandle , int , Point *);
  58. static int        MapPt2Angle(ControlHandle , Point);
  59.  
  60. /**********************************************************************/
  61. pascal long main(int varCode, ControlHandle theControl,
  62.                  int message, long param)
  63. {
  64.     Handle h;
  65.  
  66.     switch (message) {
  67.     
  68.         case drawCntl:
  69.             Draw( theControl, param);
  70.             return(0);
  71.             break;
  72.             
  73.         case testCntl:
  74.             return Test( theControl, param);
  75.             break;
  76.             
  77.         case calcCRgns:        /* 24-bit mode way of getting regions */
  78.             if ( param & 0x80000000 )
  79.             {
  80.                 param = (long)StripAddress( (Ptr)param);
  81.                 SetEmptyRgn( (Handle)param);
  82.             }
  83.             else
  84.             {
  85.                 param = (long)StripAddress( (Ptr)param);
  86.                 RectRgn( (Handle)param, &(**theControl).contrlRect);
  87.             }
  88.             return param;
  89.             break;
  90.         
  91.         case calcThumbRgn:    /* used in 32-bit mode instead */
  92.             {
  93.                 param = (long)StripAddress( (Ptr)param);
  94.                 SetEmptyRgn( (Handle)param);
  95.             }
  96.             return param;
  97.             break;
  98.             
  99.         case calcCntlRgn:    /* used in 32-bit mode instead */
  100.             {
  101.                 param = (long)StripAddress( (Handle)param);
  102.                 RectRgn( (Handle)param, &(**theControl).contrlRect);
  103.             }
  104.             return param;
  105.             break;
  106.         
  107.         case initCntl:    /* load the picture resource */
  108.             {
  109.                 Rect r = (**theControl).contrlRect;
  110.                 
  111.                 h = (**theControl).contrlData = NewHandle( sizeof(CI));
  112.     
  113.                 (**(CIH)h).thePict = (PicHandle)GetPicture( kMeterPict);
  114.                 ResetPoints( theControl);
  115.                 (**(CIH)h).lastValue = (**theControl).contrlValue;
  116.                 MapValue2Pt( theControl, &(**(CIH)h).lastPt);
  117.                             
  118.                 (**theControl).contrlAction = NULL;    /*  */
  119.                 return(0);
  120.                 break;
  121.             }
  122.             
  123.         case dispCntl:    /* dispose of picture resource */
  124.             h = (**theControl).contrlData;
  125.             ReleaseResource( (**(CIH)h).thePict);    /* delete picture */
  126.             DisposHandle( h);                    /* delete data block */
  127.             (**theControl).contrlData = NULL;
  128.             return(0);
  129.             break;
  130.             
  131.         case posCntl:        /* define this to position ind after drag */
  132.             return(0);
  133.             break;
  134.             
  135.         case thumbCntl:
  136.             {
  137.                 ((TRP)param)->limitRect    = (**theControl).contrlRect;
  138.                 ((TRP)param)->slopRect    = (**theControl).contrlRect;
  139.                 ((TRP)param)->axis        = noConstraint;
  140.             }
  141.             return(0);
  142.             break;
  143.             
  144.         case dragCntl:
  145.             if (param != 0L)        /* drag just indicator */
  146.             {
  147.                 DragNeedle( theControl);
  148.                 return(1);
  149.             }
  150.             else            /* drag entire control */
  151.             {
  152.                 return(0);
  153.             }
  154.             break;
  155.             
  156.         case autoTrack:
  157.             return(0);
  158.             break;
  159.     } /* switch() */
  160.     return(0);        /* just in case; should not be reached */
  161. } /* main() */
  162.  
  163.  
  164. /******************
  165. ** Draw()
  166. **
  167. ** Draws at least the needle of the control, and also
  168. ** draws the entire control when told.
  169. *******************/
  170.  
  171. static void Draw(ControlHandle theControl, long param)
  172. {
  173.     Rect r = (**theControl).contrlRect;
  174.     GrafPtr savedPort;
  175.     CIH cData = (CIH)(**theControl).contrlData;
  176.     Point newpt;
  177.     
  178.  
  179.     if ( (**theControl).contrlVis == 0)
  180.         return;        /* do nothing */
  181.  
  182.     GetPort(&savedPort);
  183.     SetPort( (**theControl).contrlOwner);    /* owner window */
  184.     
  185.     /*** which parts to draw... if param=0, draw them all */
  186.     
  187.     if ( param == 0 )    /* draw basic control shape */
  188.     {
  189.         CIH    cData = (CIH)(**theControl).contrlData;
  190.         ResetPoints(theControl);
  191.         DrawPicture( (**cData).thePict, &r);
  192.         FrameRect(&r);
  193.     }
  194.     
  195.     /********* Draw the indicator - the meter's "needle" *************/
  196.     MapValue2Pt( theControl, &newpt);
  197.     DrawNeedle( theControl, newpt, (**cData).lastPt);
  198.     (**cData).lastPt = newpt;            /* update the last point drawn */
  199.     
  200.     /********* Now print the value of the control at the bottom. ****/
  201.     DrawValue( theControl, (**theControl).contrlValue,
  202.                 (**cData).lastValue);
  203.     (**cData).lastValue = (**theControl).contrlValue;
  204.     
  205.     SetPort( savedPort);
  206. } /* Draw() */
  207.  
  208.  
  209.  
  210. /***********************
  211. ** Test()
  212. **
  213. ** This is called when the function
  214. ** receives the testCntl message.
  215. ** It determines where in the control
  216. ** the point lies, and returns the
  217. ** corresponding part code.
  218. *************************/
  219.  
  220. static long Test(ControlHandle theControl, long param)
  221. {
  222.     Point testPt, current;
  223.     int currang, testang;    /* current and test points' angles */
  224.     
  225.     testPt = *(Point*)¶m;    /* fool C typechecking */
  226.  
  227.     if ( !PtInRect( testPt, &(**theControl).contrlRect) )
  228.         return 0L;
  229.     
  230.     MapValue2Pt( theControl, ¤t);
  231.     currang = MapPt2Angle(theControl, current);
  232.     testang = MapPt2Angle(theControl, testPt);    /* angles are in [0,270] */
  233.  
  234.     if (testang == -1)            /* then it's outside needle sweep */
  235.         return 0L;
  236.     
  237.     if ( testang < currang - 3)
  238.         return inTurnDown;
  239.     else if (testang > currang + 3)
  240.         return inTurnUp;
  241.     else
  242.         return inNeedle;        /* angle offsets allow a total range of 7° */
  243.  
  244. } /* Test() */
  245.  
  246.  
  247. /***********************
  248. ** DragNeedle()
  249. **
  250. ** This watches the mouse position and whenever
  251. ** the position changes, it updates the needle
  252. ** position and the control's value.  Everything
  253. ** is redrawn.  When the mouse button is
  254. ** released and it's in the legal control region,
  255. ** the control's value is finally updated and redrawn.
  256. ************************/
  257.  
  258. static void DragNeedle(ControlHandle theControl)
  259. {
  260.     Point newPos, oldPos, oldMouse;
  261.     int angle, newValue, oldValue, valid=0;
  262.     CIH    cData = (CIH)(**theControl).contrlData;
  263.     
  264.     oldValue = (**cData).lastValue;
  265.     
  266.     GetMouse( &oldMouse);
  267.     oldPos = (**cData).lastPt;
  268.  
  269.     while ( StillDown() )
  270.     {
  271.         GetMouse( &newPos);
  272.         
  273.         /** Did the mouse move?  If so, get new position and update. **/
  274.         if    ( (newPos.h != oldMouse.h) || (newPos.v != oldMouse.v))
  275.         {
  276.             oldMouse = newPos;
  277.             valid = PtInRect( newPos, &(**theControl).contrlRect) &&
  278.                     ((angle = MapPt2Angle( theControl, newPos)) != -1);
  279.             
  280.             if (valid)
  281.             {
  282.                 MapAngle2Pt( theControl, angle - 135, &newPos);
  283.                 DrawNeedle( theControl, newPos, oldPos);
  284.     
  285.                 newValue = MapAngle2Value( theControl, angle);
  286.                 DrawValue( theControl, newValue, oldValue);
  287.     
  288.                 oldValue = newValue;
  289.                 oldPos = newPos;
  290.             }
  291.             else    /* it's not in sweep region */
  292.             {
  293.                 /* draw needle's current value */
  294.                 DrawNeedle( theControl, (**cData).lastPt, oldPos);
  295.                 oldPos = (**cData).lastPt;
  296.                 DrawValue(  theControl, (**cData).lastValue, oldValue);
  297.                 oldValue = (**cData).lastValue;
  298.             } /* if */
  299.         } /* if mouse position changed */
  300.     } /* while button is pressed */
  301.     
  302.     if (valid)    /* then update control to new value */
  303.     {
  304.         (**theControl).contrlValue = newValue;
  305.         MapAngle2Pt( theControl, angle - 135, &newPos);
  306.         (**cData).lastPt = newPos;
  307.         
  308.         newValue = MapAngle2Value( theControl, angle);
  309.         (**cData).lastValue = newValue;
  310.  
  311.     }
  312.     Draw( theControl, 0);    /* simulate a Ctrl Mgr call. */
  313.     return;
  314. } /* DragNeedle() */
  315.  
  316.  
  317.  
  318. /**********************************************************************/
  319. /** Following are some miscellaneous routines we need for utility    **/
  320. /** purposes.                                                        **/
  321. /**********************************************************************/
  322.  
  323.  
  324. /***********************
  325. ** DrawNeedle()
  326. **
  327. ** Draws the new needle position.  All
  328. ** this drawing makes for a fast refresh since
  329. ** we don't have to erase a large area explicitly.
  330. ************************/
  331.  
  332. static void DrawNeedle(ControlHandle theControl, Point newPos, Point oldPos)
  333. {
  334.     CIH cData = (CIH)(**theControl).contrlData;
  335.  
  336.     /*** draw the new needle ****/
  337.     PenSize(1,1);
  338.     MoveTo( (**cData).center.h, (**cData).center.v);
  339.     LineTo( newPos.h, newPos.v);
  340.     
  341.     /********** Erase last needle **********/
  342.     PenMode( notPatCopy);
  343.     MoveTo( (**cData).center.h, (**cData).center.v);
  344.     LineTo( oldPos.h, oldPos.v);
  345.  
  346.     /********** Draw the new needle to be sure *********************/
  347.     /*** This ISN'T wasteful.  If the control is moved, bad      ***/
  348.     /*** things happen, like needle doesn't reappear if we don't ***/
  349.     /*** draw it again.                                          ***/
  350.     
  351.     PenMode( patCopy);
  352.     MoveTo( (**cData).center.h, (**cData).center.v);
  353.     LineTo( newPos.h, newPos.v);
  354.  
  355. } /* DrawNeedle() */
  356.  
  357. /***************************
  358. ** DrawValue()
  359. **
  360. ** Updates the value readout at the bottom of the meter
  361. ** to reflect this value/corresponding needle position.
  362. ****************************/
  363. static void DrawValue(ControlHandle theControl, int newVal, int oldVal)
  364. {
  365.     Str255    vstrnew, vstrold;
  366.     int oldTmode, newwid, oldwid, xold, xnew, y;
  367.     CIH cData = (CIH)(**theControl).contrlData;
  368.  
  369.     NumToString( (long)newVal, vstrnew);
  370.     newwid = StringWidth( vstrnew);
  371.     NumToString( (long)oldVal, vstrold);
  372.     oldwid = StringWidth( vstrold);
  373.     
  374.     oldTmode = ((**theControl).contrlOwner)->txMode;    /* save old mode */
  375.     xold = (**cData).center.h - oldwid/2;
  376.     xnew = (**cData).center.h - newwid/2;
  377.     y    = (**theControl).contrlRect.bottom - 2;
  378.     
  379.     TextMode( srcXor);
  380.     MoveTo( xnew, y);
  381.     DrawString( vstrnew);
  382.  
  383.     MoveTo( xold, y);
  384.     DrawString( vstrold);
  385.  
  386.     TextMode( srcOr);
  387.     MoveTo( xnew, y);
  388.     DrawString( vstrnew);
  389.     TextMode( oldTmode);                /* restore original mode */
  390.  
  391. } /* DrawValue() */
  392.  
  393.  
  394. /***************************
  395. ** ResetPoints()
  396. **
  397. ** It calculates the new center of the control
  398. ** and updates radius and center
  399. ****************************/
  400. static void ResetPoints(ControlHandle theControl)
  401. {
  402.     Rect r = (**theControl).contrlRect;
  403.     Point newctr, oldctr, offset;
  404.     CIH h = (CIH)(**theControl).contrlData;    /* get our data */
  405.     
  406.     oldctr = (**(CIH)h).center;
  407.     
  408.     newctr.h = (r.left + r.right) /2;
  409.     newctr.v = (r.top + r.bottom) /2;
  410.     (**(CIH)h).center = newctr;    /* new values */
  411.     (**(CIH)h).radius.h = 3 * (newctr.h - r.left) /4;
  412.     (**(CIH)h).radius.v = 3 * (newctr.v - r.top) /4;
  413.  
  414.     offset.h = newctr.h - oldctr.h;
  415.     offset.v = newctr.v - oldctr.v;
  416.     
  417.     (**(CIH)h).lastPt.h += offset.h;        /* Also move last needle point */
  418.     (**(CIH)h).lastPt.v += offset.v;        /* to match new meter location */
  419. } /* ResetPoints() */
  420.  
  421.  
  422. /****************
  423. ** MapValue2Pt()
  424. **
  425. ** Given a point and a control, this proc
  426. ** calculates the point to plot given the
  427. ** value of the control.  The point is 
  428. ** the location on the edge of the needle sweep area
  429. ** to connect to the center. (pt contains new point to plot.)
  430. *****************/
  431. static void MapValue2Pt(ControlHandle theControl, Point *pt)
  432. {
  433.     MapAngle2Pt( theControl, MapValue2Angle( theControl),  pt);
  434. } /* MapValue2Pt() */
  435.  
  436.  
  437. /**********************
  438. ** MapValue2Angle()
  439. **
  440. ** given the control, return the angle
  441. ** represented by the current value.
  442. ***********************/
  443. static int MapValue2Angle(ControlHandle theControl)
  444. {
  445.     int setting, cmin, cmax;
  446.     setting = ( **theControl).contrlValue;
  447.     cmin =    ( **theControl).contrlMin;
  448.     cmax =    ( **theControl).contrlMax;
  449.     return (int)((double)(setting - cmin)/(double)(cmax - cmin) * 
  450.             (double)270.0) - 135;
  451. }
  452.  
  453.  
  454. /***********************
  455. ** MapAngle2Value()
  456. **
  457. ** This calculates the meter's value that corresponds to the given
  458. ** needle angle.
  459. ************************/
  460. static int MapAngle2Value(ControlHandle theControl, int angle)
  461. {
  462.     int crange;
  463.     double percent;
  464.     
  465.     crange = (**theControl).contrlMax - (**theControl).contrlMin;
  466.     percent = (double)angle/(double)270.0;
  467.     return (int)(percent * (double)crange) + (**theControl).contrlMin;
  468. } /* MapAngle2Value() */
  469.  
  470.  
  471. /***********************
  472. ** MapAngle2Pt()
  473. **
  474. ** Given a handle to a Control,
  475. ** and the angle of the current value, calculate and
  476. ** set pt to the point along the edge of the needle sweep
  477. ** area. NOTE: the angle passed here is in degrees and we
  478. ** have to convert it to radians.
  479. ************************/
  480. static void MapAngle2Pt(ControlHandle theControl, int angle, Point *pt)
  481. {
  482.     CIH cData = (CIH)(**theControl).contrlData;
  483.     double dangle;
  484.     dangle = (double)angle * (double)(3.1415926)/(double)180.0;
  485.     pt->h = (**cData).center.h +
  486.               (int)( ((**cData).radius.h - 1) * sin(dangle));
  487.     pt->v = (**cData).center.v - 
  488.               (int)( ((**cData).radius.v - 1) * cos(dangle));
  489. }
  490.  
  491.  
  492. /************************
  493. ** MapPt2Angle()
  494. **
  495. ** Given the control's rectangle and point
  496. ** in that rectangle, return the angle of
  497. ** that point normalized in the range [0, 270].
  498. ** If point is outside the desired area, return -1.
  499. *************************/
  500. static int MapPt2Angle(ControlHandle theControl, Point pt)
  501. {
  502.     int iangle;
  503.     
  504.     PtToAngle( &(**theControl).contrlRect, pt, &iangle);
  505.     if ( !((136 <= iangle) && (iangle <= 224)) )
  506.     {    iangle += 135;        /* put it in [0, 270] range */
  507.         iangle %= 360;
  508.     }
  509.     else
  510.         return(-1);        /* out of area, so return -1 */
  511.     
  512.     return (iangle);
  513. } /* MapPt2Angle() */
  514.  
  515.  
  516. /* fin */
  517.