home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / c / xv221src / xvdial.c < prev    next >
C/C++ Source or Header  |  1992-02-27  |  13KB  |  453 lines

  1. /* 
  2.  * xvdial.c - DIAL handling functions
  3.  *
  4.  * callable functions:
  5.  *
  6.  *   DCreate()   -  creates a dial
  7.  *   DSetRange() -  sets min/max/current values of control
  8.  *   DSetVal()   -  sets value of control 
  9.  *   DSetActive() - turns dial '.active' on and off
  10.  *   DRedraw()   -  redraws the dial
  11.  *   DTrack()    -  called when clicked.  Operates control 'til mouseup
  12.  */
  13.  
  14. /*
  15.  * Copyright 1989, 1990, 1991, 1992 by John Bradley and
  16.  *                       The University of Pennsylvania
  17.  *
  18.  * Permission to use, copy, and distribute for non-commercial purposes,
  19.  * is hereby granted without fee, providing that the above copyright
  20.  * notice appear in all copies and that both the copyright notice and this
  21.  * permission notice appear in supporting documentation.
  22.  *
  23.  * The software may be modified for your own purposes, but modified versions
  24.  * may not be distributed.
  25.  *
  26.  * This software is provided "as is" without any expressed or implied warranty.
  27.  *
  28.  * The author may be contacted via:
  29.  *    US Mail:   John Bradley
  30.  *               GRASP Lab, Room 301C
  31.  *               3401 Walnut St.
  32.  *               Philadelphia, PA  19104
  33.  *
  34.  *    Phone:     (215) 898-8813
  35.  *    EMail:     bradley@cis.upenn.edu
  36.  */
  37.  
  38.  
  39. #include "xv.h"
  40. #include "bitmaps.h"
  41.  
  42. static Pixmap cw1Pix, ccw1Pix;  /* up/down arrows */
  43. static Pixmap cw2Pix, ccw2Pix;  /* up/down page arrows */
  44. static int    pixmaps_built=0;   /* true if pixmaps created already */
  45.  
  46. #define PW dial_cw1_width        /* size of arrows */
  47. #define PH dial_cw1_height
  48.  
  49. /* dial regions */
  50. #define INCW1  0
  51. #define INCCW1 1
  52. #define INCW2  2
  53. #define INCCW2 3
  54. #define INDIAL 4
  55.  
  56. #define INC1WAIT 150   /* milliseconds to wait after initial hit */
  57. #define INC2WAIT 150   /* milliseconds to wait between increments */
  58. #define DEG2RAD (3.14159265 / 180.0)
  59. #define RAD2DEG (180.0 / 3.14159265)
  60.  
  61.  
  62. /* local functions */
  63. #ifdef __STDC__
  64. static int  whereInDial(DIAL *, int, int);
  65. static void drawArrow(DIAL *);
  66. static void drawValStr(DIAL *);
  67. static void invertButt(DIAL *, int);
  68. static int  computeDialVal(DIAL *, int, int);
  69. static void dimDial(DIAL *);
  70. #else
  71. static int  whereInDial(), computeDialVal();
  72. static void drawArrow(), drawValStr(), invertButt(), dimDial();
  73. #endif
  74.  
  75.  
  76. /***************************************************/
  77. void DCreate(dp, parent, x, y, w, h, minv, maxv, curv, page, 
  78.               fg, bg, title, units)
  79. DIAL         *dp;
  80. Window        parent;
  81. int           x,y,w,h,minv,maxv,curv,page;
  82. unsigned long fg,bg;
  83. char         *title, *units;
  84. {
  85.  
  86.   if (!pixmaps_built) {
  87.     cw1Pix   = XCreatePixmapFromBitmapData(theDisp, parent, 
  88.         dial_cw1_bits, PW, PH, fg, bg, dispDEEP);
  89.     ccw1Pix  = XCreatePixmapFromBitmapData(theDisp, parent, 
  90.             dial_ccw1_bits, PW, PH, fg, bg, dispDEEP);
  91.     cw2Pix   = XCreatePixmapFromBitmapData(theDisp, parent, 
  92.                 dial_cw2_bits, PW, PH, fg, bg, dispDEEP);
  93.     ccw2Pix  = XCreatePixmapFromBitmapData(theDisp, parent, 
  94.             dial_ccw2_bits, PW, PH, fg, bg, dispDEEP);
  95.   }
  96.  
  97.   dp->w     = w;
  98.   dp->h     = h;
  99.   dp->fg    = fg;
  100.   dp->bg    = bg;
  101.   dp->title = title;
  102.   dp->units = units;
  103.   dp->active = 1;
  104.   dp->drawobj = NULL;
  105.  
  106.   if (w < h-24-16) dp->rad = (w - 8) / 2;
  107.            else dp->rad = (h - 24 - 16 - 8) / 2;
  108.   dp->cx = w / 2;
  109.   dp->cy = dp->rad + 4 + 16;
  110.  
  111.   dp->bx[INCCW1] = 4;       dp->by[INCCW1] = h - 4 - 20;
  112.   dp->bx[INCCW2] = 4;       dp->by[INCCW2] = h - 4 - 10;
  113.   dp->bx[INCW1]  = w-14-4;  dp->by[INCW1]  = h - 4 - 20;
  114.   dp->bx[INCW2]  = w-14-4;  dp->by[INCW2]  = h - 4 - 10;
  115.  
  116.   dp->win = XCreateSimpleWindow(theDisp, parent,x,y,w,h,1,fg,bg);
  117.   if (!dp->win) FatalError("can't create dial window");
  118.  
  119.   DSetRange(dp, minv, maxv, curv, page);
  120.   XSelectInput(theDisp, dp->win, ExposureMask | ButtonPressMask);
  121. }
  122.  
  123.  
  124. /***************************************************/
  125. void DSetRange(dp, minv, maxv, curv, page)
  126. DIAL *dp;
  127. int   minv, maxv, curv, page;
  128. {
  129.   if (maxv<minv) maxv=minv;
  130.   dp->min = minv;    dp->max = maxv;    dp->page = page;
  131.   dp->active =  (minv < maxv);
  132.  
  133.   DSetVal(dp, curv);
  134. }
  135.  
  136.  
  137. /***************************************************/
  138. void DSetVal(dp, curv)
  139. DIAL *dp;
  140. int   curv;
  141. {
  142.   RANGE(curv, dp->min, dp->max);   /* make sure curv is in-range */
  143.  
  144.   if (curv == dp->val) return;
  145.  
  146.   /* erase old arrow */
  147.   XSetForeground(theDisp, theGC, dp->bg); 
  148.   drawArrow(dp);
  149.  
  150.   dp->val = curv;
  151.  
  152.   /* draw new arrow and string */
  153.   XSetForeground(theDisp, theGC, dp->fg);
  154.   XSetBackground(theDisp, theGC, dp->bg); 
  155.   drawArrow(dp);
  156.   drawValStr(dp);
  157.   if (!dp->active) dimDial(dp);
  158.  
  159.   XFlush(theDisp);
  160. }
  161.  
  162.  
  163. /***************************************************/
  164. void DSetActive(dp, i)
  165. DIAL *dp;
  166. int   i;
  167. {
  168.   if (i == dp->active) return;
  169.  
  170.   dp->active = i;
  171.   XClearWindow(theDisp, dp->win);
  172.   DRedraw(dp);
  173.   XFlush(theDisp);
  174. }
  175.  
  176.  
  177. /***************************************************/
  178. void DRedraw(dp)
  179. DIAL *dp;
  180. {
  181.   double tsize;
  182.   int    i, rad, cx, cy, x1, y1, x2, y2;
  183.  
  184.   rad = dp->rad;  cx = dp->cx;  cy = dp->cy;
  185.  
  186.   XSetForeground(theDisp, theGC, dp->fg);
  187.   XSetBackground(theDisp, theGC, dp->bg);
  188.  
  189.   /* draw title */
  190.   CenterString(dp->win, dp->title, dp->w/2, 8);
  191.  
  192.   /* draw tick marks around circle */
  193.   for (i = -60; i<=240; i += 10) {
  194.     if (i%60 == 0) tsize = 0.85;  else tsize = 0.95;
  195.     x1 = cx + (int) ((double) rad * cos(i * DEG2RAD));
  196.     y1 = cy - (int) ((double) rad * sin(i * DEG2RAD));
  197.     x2 = cx + (int) ((double) rad * tsize  * cos(i*DEG2RAD));
  198.     y2 = cy - (int) ((double) rad * tsize  * sin(i*DEG2RAD));
  199.  
  200.     XDrawLine(theDisp, dp->win, theGC, x1, y1, x2, y2);
  201.   }
  202.  
  203.   drawArrow(dp);
  204.  
  205.   /* draw the cw/ccw controls */
  206.   for (i=0; i<4; i++)
  207.     XDrawRectangle(theDisp, dp->win, theGC, dp->bx[i], dp->by[i], 14, 10);
  208.  
  209.   XCopyArea(theDisp, ccw1Pix, dp->win, theGC, 0, 0, PW, PH,
  210.         dp->bx[INCCW1]+(15-PW)/2, dp->by[INCCW1]+(11-PH)/2);
  211.  
  212.   XCopyArea(theDisp, ccw2Pix, dp->win, theGC, 0, 0, PW, PH,
  213.         dp->bx[INCCW2]+(15-PW)/2, dp->by[INCCW2]+(11-PH)/2);
  214.  
  215.   XCopyArea(theDisp, cw1Pix, dp->win, theGC, 0, 0, PW, PH,
  216.         dp->bx[INCW1]+(15-PW)/2, dp->by[INCW1]+(11-PH)/2);
  217.  
  218.   XCopyArea(theDisp, cw2Pix, dp->win, theGC, 0, 0, PW, PH,
  219.         dp->bx[INCW2]+(15-PW)/2, dp->by[INCW2]+(11-PH)/2);
  220.  
  221.   drawValStr(dp);
  222.  
  223.   if (!dp->active) dimDial(dp);
  224. }
  225.  
  226.  
  227. /***************************************************/
  228. int DTrack(dp, mx, my)
  229. DIAL *dp;
  230. int mx,my;
  231. {
  232.   Window       rW,cW;
  233.   int          rx,ry, x,y, ipos, pos, lit, i, origval;
  234.   unsigned int mask;
  235.  
  236.   if (!dp->active) return 0;
  237.  
  238.   XSetForeground(theDisp, theGC, dp->fg);
  239.   XSetBackground(theDisp, theGC, dp->bg);
  240.  
  241.   /* determine in which of the five regions of the dial the mouse
  242.      was clicked (cw1, ccw1, cw2, ccw2, dial-proper) */
  243.  
  244.   ipos = whereInDial(dp, mx, my);
  245.   if (ipos<0) return 0;          /* didn't hit any of the actual controls */
  246.  
  247.   origval = dp->val;
  248.  
  249.   /* light up appropriate button, if it's in one of them */
  250.   if (ipos != INDIAL) {
  251.     invertButt(dp, ipos);
  252.     switch (ipos) {
  253.     case INCW1:  if (dp->val < dp->max) DSetVal(dp, dp->val+1); break;
  254.     case INCW2:  if (dp->val < dp->max) DSetVal(dp, dp->val+dp->page); break;
  255.     case INCCW1: if (dp->val > dp->min) DSetVal(dp, dp->val-1); break;
  256.     case INCCW2: if (dp->val > dp->min) DSetVal(dp, dp->val-dp->page); break;
  257.     }
  258.     if (dp->drawobj != NULL) (dp->drawobj)();  
  259.     Timer(INC1WAIT);
  260.     lit = 1;
  261.   }
  262.  
  263.   else { 
  264.     i = computeDialVal(dp, mx, my);
  265.     DSetVal(dp, i);
  266.     if (dp->drawobj != NULL) (dp->drawobj)();  
  267.   }
  268.  
  269.   
  270.   /* loop until mouse is released */
  271.   while (XQueryPointer(theDisp,dp->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  272.     if (!(mask & Button1Mask)) break;    /* button released */
  273.  
  274.     if (ipos == INDIAL) {
  275.       int j;
  276.       i = computeDialVal(dp, x, y);
  277.       j = dp->val;
  278.       DSetVal(dp, i);
  279.       if (j != dp->val) {
  280.     /* track whatever dial controls */
  281.     if (dp->drawobj != NULL) (dp->drawobj)();  
  282.       }
  283.     }
  284.  
  285.     else {                            /* a button */
  286.       pos = whereInDial(dp, x, y);
  287.       if ( (pos==ipos && !lit) || (pos!=ipos && lit)) {
  288.     /* need to toggle lit state */
  289.     lit = !lit;
  290.     invertButt(dp, ipos);
  291.       }
  292.  
  293.       if (lit) {
  294.     switch (ipos) {
  295.     case INCW1:  if (dp->val < dp->max) DSetVal(dp, dp->val+1); 
  296.                  break;
  297.     case INCW2:  if (dp->val < dp->max) DSetVal(dp, dp->val+dp->page);
  298.                      break;
  299.     case INCCW1: if (dp->val > dp->min) DSetVal(dp, dp->val-1);
  300.                      break;
  301.     case INCCW2: if (dp->val > dp->min) DSetVal(dp, dp->val-dp->page);
  302.                      break;
  303.     }
  304.  
  305.     /* track whatever dial controls */
  306.     if (dp->drawobj != NULL) (dp->drawobj)();  
  307.  
  308.     Timer(INC2WAIT);
  309.       }
  310.     }
  311.     XFlush(theDisp);
  312.   }
  313.  
  314.  
  315.   /* turn off button, if lit */
  316.   if (ipos != INDIAL && lit) invertButt(dp, ipos);
  317.  
  318.   return (dp->val != origval);
  319. }
  320.  
  321.  
  322.  
  323.  
  324.  
  325. /***************************************************/
  326. static int whereInDial(dp, x, y)
  327. DIAL *dp;
  328. int x, y;
  329. {
  330.   int i;
  331.  
  332.   /* returns region * that x,y is in.  returns -1 if none */
  333.  
  334.   for (i=0; i<4; i++) 
  335.     if (PTINRECT(x,y, dp->bx[i], dp->by[i], 14, 10)) return i;
  336.  
  337.   if (PTINRECT(x,y, dp->cx - dp->rad, dp->cy - dp->rad, 
  338.            2*dp->rad, 2*dp->rad))
  339.     return INDIAL;
  340.  
  341.   return -1;
  342. }
  343.  
  344.       
  345. /***************************************************/
  346. static void drawArrow(dp)
  347. DIAL *dp;
  348. {
  349.   int i, rad, cx, cy;
  350.   XPoint arrow[4];
  351.  
  352.   rad = dp->rad;  cx = dp->cx;  cy = dp->cy;
  353.  
  354.   /* map pos (range minv..maxv) into degrees (range 240..-60) */
  355.   i = 240 + (-300 * (dp->val - dp->min)) / (dp->max - dp->min);
  356.   arrow[0].x = cx + (int) ((double) rad * .80 * cos(i * DEG2RAD));
  357.   arrow[0].y = cy - (int) ((double) rad * .80 * sin(i * DEG2RAD));
  358.   arrow[1].x = cx + (int) ((double) rad * .33 * cos((i+160) * DEG2RAD));
  359.   arrow[1].y = cy - (int) ((double) rad * .33 * sin((i+160) * DEG2RAD));
  360.   arrow[2].x = cx + (int) ((double) rad * .33 * cos((i-160) * DEG2RAD));
  361.   arrow[2].y = cy - (int) ((double) rad * .33 * sin((i-160) * DEG2RAD));
  362.   arrow[3].x = arrow[0].x;
  363.   arrow[3].y = arrow[0].y;
  364.   XDrawLines(theDisp, dp->win, theGC, arrow, 4, CoordModeOrigin);
  365. }
  366.  
  367.  
  368. /***************************************************/
  369. static void drawValStr(dp)
  370. DIAL *dp;
  371. {
  372.   int  i, x1, x2;
  373.   char foo[60], foo1[60];
  374.  
  375.   /* compute longest string necessary so we can right-align this thing */
  376.   sprintf(foo,"%d",dp->min);    x1 = strlen(foo);
  377.   sprintf(foo,"%d",dp->max);    x2 = strlen(foo);
  378.   if (dp->min < 0 && dp->max > 0) x2++;   /* put '+' at beginning */
  379.   i = x1;  if (x2>x1) i = x2;
  380.   if (dp->units) i += strlen(dp->units);
  381.  
  382.   if (dp->min < 0 && dp->max > 0) sprintf(foo,"%+d", dp->val);
  383.   else sprintf(foo,"%d", dp->val);
  384.  
  385.   if (dp->units) strcat(foo,dp->units);
  386.   foo1[0] = '\0';
  387.   if (strlen(foo)<i) {
  388.     for (i = i - strlen(foo); i>0; i--) strcat(foo1," ");
  389.   }
  390.   strcat(foo1, foo);
  391.  
  392.   XSetFont(theDisp, theGC, monofont);
  393.   XDrawImageString(theDisp, dp->win, theGC, 
  394.            dp->w/2 - XTextWidth(monofinfo, foo1, strlen(foo1))/2,
  395.            dp->h-14 - (monofinfo->ascent + monofinfo->descent)/2
  396.               + monofinfo->ascent, foo1, strlen(foo1));
  397.   XSetFont(theDisp, theGC, mfont);
  398. }
  399.  
  400.  
  401. /***************************************************/
  402. static void invertButt(dp, i)
  403. DIAL *dp;
  404. int i;
  405. {
  406.   XSetState(theDisp, theGC, dp->fg, dp->bg, GXinvert, dp->fg ^ dp->bg);
  407.   XFillRectangle(theDisp, dp->win, theGC, 
  408.          dp->bx[i]+1, dp->by[i]+1, 13, 9);
  409.   XSetState(theDisp, theGC, dp->fg, dp->bg, GXcopy, AllPlanes);
  410.   XFlush(theDisp);
  411. }
  412.  
  413.  
  414. /***************************************************/
  415. static int computeDialVal(dp, x, y)
  416. DIAL *dp;
  417. int x, y;
  418. {
  419.   int dx, dy, val;
  420.   double angle;
  421.  
  422.   /* compute dx, dy (distance from cx, cy).  Note: +dy is *up* */
  423.   dx = x - dp->cx;  dy = dp->cy - y;
  424.  
  425.   /* if too close to center, return current value to avoid 'spazzing' */
  426.   if (abs(dx) < 3 && abs(dy) < 3) return dp->val;
  427.  
  428.   /* figure out angle of vector dx,dy */
  429.   if (dx==0) {     /* special case */
  430.     if (dy>0) angle =  90.0;
  431.          else angle = -90.0;
  432.   }
  433.   else if (dx>0) angle = atan((double)  dy / (double)  dx) * RAD2DEG;
  434.   else           angle = atan((double) -dy / (double) -dx) * RAD2DEG + 180.0;
  435.     
  436.   /* map angle into range: -90..270, then into to value */
  437.   if (angle > 270.0) angle -= 360.0;
  438.   if (angle < -90.0) angle += 360.0;
  439.  
  440.   val = (int) ((dp->max - dp->min) * (240.0 - angle) / 300.0) + dp->min;
  441.  
  442.   return val;
  443. }
  444.  
  445.  
  446. /***************************************************/
  447. static void dimDial(dp)
  448. DIAL *dp;
  449. {
  450.   DimRect(dp->win, 0, 0, dp->w, dp->h, dp->bg);
  451. }
  452.  
  453.