home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1994 June / NEBULA_SE.ISO / SourceCode / MiscKit / Palettes / MiscCircularSlider / MiscCircularSlider.subproj / MiscCircularSliderCell.m < prev    next >
Encoding:
Text File  |  1994-03-28  |  11.6 KB  |  415 lines

  1. //
  2. //    MiscCircularSliderCell.m -- a SliderCell for circular sliders
  3. //        Written by Vince DeMarco and Don Yacktman, 
  4. //        Copyright (c) 1994 by Vince DeMarco and Don Yacktman.
  5. //                Version 1.0  All rights reserved.
  6. //        This notice may not be removed from this source code.
  7. //
  8. //    This object is included in the MiscKit by permission from the author
  9. //    and its use is governed by the MiscKit license, found in the file
  10. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  11. //    for a list of all applicable permissions and restrictions.
  12. //    
  13.  
  14. // This was created by modifying Vince's CircularSliderCell class and
  15. // merging it with Don's RotationSliderCell class.  Here's the info
  16. // from the original files.
  17. /*
  18.  *    Filename:    CircularSliderCell.m 
  19.  *    Created :    Sun Oct 18 16:56:38 1992 
  20.  *    Author  :    Vince DeMarco
  21.  *        <vince@whatnxt.cuc.ab.ca>
  22.  *    LastEditDate was "Wed Feb  3 17:30:28 1993"
  23.  */
  24. //    RotationSliderCell.m -- written by Don Yacktman
  25. //    Copyright 1993 by Don Yacktman.  All rights reserved.
  26.  
  27. #import "MiscCircularSliderCell.h"
  28. #import "MiscCSWraps.h"
  29. #import <math.h>
  30. #import <appkit/appkit.h>
  31.  
  32. @interface MiscCircularSliderCell(private)
  33. - _cacheImages;
  34. @end
  35.  
  36. #define _KNOB_WIDTH_FUDGE (5.0 / 8.0)
  37. #define _KNOB_Y_FUDGE (- 1.0)
  38. #define _KNOB_X_FUDGE (  0.0)
  39. #define _BEZELS_FUDGE (  4.0)
  40. @implementation MiscCircularSliderCell
  41.  
  42. inline static float angle(float x, float y)
  43. { // this is a little cleaner than Vince's original routine.  :-)
  44.     double result = atan2(y, x) * (180 / M_PI);
  45.     if (result < 0) result += 360.0; // keep in 0-360 degree range
  46.     return result;
  47. }
  48.  
  49. inline static void xycoord(float angle, float radius,float *x, float *y)
  50. { // the inverse of the above; given angle and radius, determines x & y
  51.     *x = radius * (float)cos((M_PI / 180) * angle);
  52.     *y = radius * (float)sin((M_PI / 180) * angle);
  53. }
  54.  
  55. // Convert between a 0-360 degree angle and the slider's position.  The
  56. // angle is used as the parameter to pass to the pswrap that does the drawing.
  57. // These two #defines are inverses of each other.
  58. #define currentangle(pos, min, max) (360.0 * ((pos) - (min)) / ((max) - (min)))
  59. #define position(angle, min, max) ((angle) * ((max) - (min)) / 360.0 + (min))
  60.  
  61. - init
  62. {
  63.     self = [super init];
  64.     minValue = 0.0;
  65.     value = 45.0;
  66.     maxValue = 360.0;
  67.     radius = 50.0; sliderPathWidth = 10.0; 
  68.     center.x = radius;
  69.     center.y = radius;
  70.     style = MISC_SLIDER_STYLE;
  71.     jumpToMousePoint = YES;
  72.     hidden = NO;
  73.     [self _cacheImages];
  74.     return self;
  75. }
  76.  
  77. - awake
  78. {
  79.     id ret = [super awake];
  80.     [self _cacheImages];
  81.     return ret;
  82. }
  83.  
  84. #define _SETUPIMAGE PSsetgray(0.0); PSsetalpha(0.0); \
  85.         NXRectFill(&frame); PSsetalpha(1.0)
  86.  
  87. - _cacheImages
  88. {    // builds the cached slider, etc., if necessary.  (ie, size changed
  89.     // or no cache created yet... *****
  90.     NXSize bezelSize = { radius * 2, radius * 2 }; NXPoint t;
  91.     NXRect frame = { { 0.0, 0.0 }, { center.x * 2, center.y * 2 } };
  92.     NXRect knobRect = { { center.x - sliderPathWidth / 4 + 1,
  93.             center.y - sliderPathWidth / 4 - 2 },
  94.             { sliderPathWidth * 1.5 + 1, sliderPathWidth * 1.5 + 2 } };
  95.  
  96.     xycoord(45.0, radius - (sliderPathWidth + 2), &(t.x), &(t.y));
  97.     NX_X(&knobRect) += t.x; NX_Y(&knobRect) += t.y;
  98.  
  99.     if (disabledBezelImage) [disabledBezelImage free];
  100.     disabledBezelImage = [[NXImage alloc] initSize:&bezelSize];
  101.     [disabledBezelImage lockFocus];
  102.     PSgsave(); _SETUPIMAGE;
  103.     MiscCSDrawBezel(0, 0, radius, sliderPathWidth, 0.666);
  104.     PSgrestore();
  105.     [disabledBezelImage unlockFocus];
  106.  
  107.     if (enabledBezelImage) [enabledBezelImage free];
  108.     enabledBezelImage = [[NXImage alloc] initSize:&bezelSize];
  109.     [enabledBezelImage lockFocus];
  110.     PSgsave(); _SETUPIMAGE;
  111.     MiscCSDrawBezel(0, 0, radius, sliderPathWidth, 0.5);
  112.     PSgrestore();
  113.     [enabledBezelImage unlockFocus];
  114.  
  115.     if (tempImage) [tempImage free];
  116.     tempImage = [[NXImage alloc] initSize:&bezelSize];
  117.     [tempImage lockFocus];
  118.     PSgsave(); _SETUPIMAGE;
  119.     MiscCSDrawKnob(0, 0, radius, sliderPathWidth, 45);
  120.     PSgrestore();
  121.     [tempImage unlockFocus];
  122.     if (knobImage) [knobImage free];
  123.     knobImage = [[NXImage alloc] initFromImage:tempImage rect:&knobRect];
  124.     return self;
  125. }
  126.  
  127. - setSliderPathWidth:(float)val
  128. {
  129.     if (val == sliderPathWidth) return self;
  130.     sliderPathWidth = val;
  131.     [self _cacheImages];
  132.     return self;
  133. }
  134.  
  135. - (float)sliderPathWidth
  136. {
  137.     return sliderPathWidth;
  138. }
  139.  
  140. - setSliderStyle:(MiscCircularSliderStyle)aBOOL
  141. {
  142.     if (aBOOL == style) return self;
  143.     style = aBOOL;
  144.     if ((style == MISC_SLIDER_STYLE) || (style == MISC_PIECHART_STYLE)) {
  145.         jumpToMousePoint = YES;
  146.         if (style == MISC_SLIDER_STYLE) [self _cacheImages];
  147.     } else jumpToMousePoint = NO;
  148.     return self;
  149. }
  150.  
  151. - (MiscCircularSliderStyle)sliderStyle
  152. {
  153.     return style;
  154. }
  155.  
  156. - setJumpToMousePoint:(BOOL)aBOOL
  157. {
  158.     jumpToMousePoint = aBOOL;
  159.     return self;
  160. }
  161.  
  162. - (BOOL)jumpToMousePoint
  163. {
  164.     return jumpToMousePoint;
  165. }
  166.  
  167. - setHidden:(BOOL)flag
  168. {
  169.     hidden = flag;
  170.     return self;
  171. }
  172.  
  173. - (BOOL)hidden
  174. {
  175.     return hidden;
  176. }
  177.  
  178. - read:(NXTypedStream *)stream
  179. {
  180.     [super read:stream];
  181.     NXReadTypes(stream, "iccff", &style, &jumpToMousePoint, &hidden, &radius, &sliderPathWidth);
  182.     NXReadPoint(stream, ¢er);
  183.     return self;
  184. }
  185.  
  186. - write:(NXTypedStream *)stream
  187. {
  188.     [super write:stream];
  189.     NXWriteTypes(stream, "iccff", &style, &jumpToMousePoint, &hidden , &radius, &sliderPathWidth);
  190.     NXWritePoint(stream, ¢er);
  191.     return self;
  192. }
  193.  
  194. // overridden methods here:
  195. - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
  196. {
  197.     theSize->width = NX_WIDTH(aRect);
  198.     theSize->height = NX_HEIGHT(aRect);
  199.  
  200.     if (style != MISC_SLIDER_STYLE) return self;
  201.     // the "slider looking" style should always be a perfect circle.
  202.     if (theSize->height < theSize->width) theSize->width = theSize->height;
  203.     else theSize->height = theSize->width;
  204.     return self;
  205. }
  206.  
  207. #define _FINDXYFROMPOINT(z) if (imFlipped) y = center.y - (z)->y; \
  208.         else y = (z)->y - center.y; \
  209.         x = (z)->x - center.x
  210.  
  211. - (BOOL)startTrackingAt:(const NXPoint *)startPoint inView:controlView
  212. { // make sure we're within the slider's "hot" area, a circle (or ring)
  213.     float x, y, rad, ir;
  214.  
  215.     _FINDXYFROMPOINT(startPoint);
  216.     _offsetAng = angle(x, y) - currentangle(value, minValue, maxValue);
  217.     rad = x * x + y * y;
  218.     if (rad > radius * radius) return NO; // outside
  219.     ir = radius - sliderPathWidth - 3.0;
  220.     if ((rad < ir * ir) && (style == MISC_SLIDER_STYLE)) return NO;
  221.     return [super startTrackingAt:startPoint inView:controlView];
  222. }
  223.  
  224. - (BOOL)continueTracking:(const NXPoint *)lastPoint
  225.         at:(const NXPoint *)currentPoint inView:controlView
  226. {
  227.     NXPoint pos; float x, y, oldx, oldy, curAng, oldAng;
  228.  
  229.     if (hidden) {
  230.         PSsetgray(0.666); NXRectFill(&_lastFrame);
  231.         return NO;
  232.     }
  233.     // get the current angle of the mouse pointer
  234.     _FINDXYFROMPOINT(currentPoint);
  235.     curAng = angle(x, y);
  236.     if (!jumpToMousePoint) {
  237.         curAng -= _offsetAng;
  238.         if (curAng < 0) curAng += 360.0;
  239.     }
  240.     // get the last angle of the mouse pointer
  241.     oldAng = currentangle(value, minValue, maxValue);
  242.  
  243.     xycoord(curAng, radius - sliderPathWidth / 2 - 2, &(pos.x), &(pos.y));
  244.     pos.x += (center.x - sliderPathWidth * _KNOB_WIDTH_FUDGE + _KNOB_X_FUDGE);
  245.     pos.y += (center.y - sliderPathWidth * _KNOB_WIDTH_FUDGE + _KNOB_Y_FUDGE);
  246.     value = position(curAng, minValue, maxValue);
  247.  
  248.     xycoord(oldAng, radius - sliderPathWidth / 2 - 2, &oldx, &oldy);
  249.     oldx += (center.x - sliderPathWidth * _KNOB_WIDTH_FUDGE + _KNOB_X_FUDGE);
  250.     oldy += (center.y - sliderPathWidth * _KNOB_WIDTH_FUDGE + _KNOB_Y_FUDGE);
  251.  
  252.     [[controlView window] disableFlushWindow];
  253.     PSgsave();
  254.     if (imFlipped) {
  255.         NXRect aFrame;
  256.         [controlView getFrame:&aFrame];
  257.         PSscale(1.0, -1.0);
  258.         PStranslate(0.0, -NX_HEIGHT(&aFrame));
  259.     }
  260.     if (style == MISC_SLIDER_STYLE) {
  261.         NXRect eraseRect;
  262.         id bezel = (cFlags1.disabled ? disabledBezelImage : enabledBezelImage);
  263.         NXSetRect(&eraseRect, oldx, oldy,
  264.                 sliderPathWidth * 1.5 + 2, sliderPathWidth * 1.5 + 2);
  265.         NXIntegralRect(&eraseRect);
  266.         NXInsetRect(&eraseRect, -4.0, -4.0);
  267.         [bezel composite:NX_SOVER fromRect:&eraseRect
  268.                 toPoint:&(eraseRect.origin)];
  269.         [knobImage composite:NX_SOVER toPoint:&pos];
  270.     } else {
  271.         [self renderAll];
  272.     }
  273.     PSgrestore();
  274.     [[[controlView window] reenableFlushWindow] flushWindow];
  275.     NXPing();
  276.     return YES;
  277. }
  278.  
  279. - drawBarInside:(const NXRect *)cellFrame flipped:(BOOL)flipped
  280. {
  281.     NXRect frame = { { 0.0, 0.0 },
  282.             { NX_WIDTH(cellFrame), NX_HEIGHT(cellFrame) } };
  283.     id bezel = (cFlags1.disabled ? disabledBezelImage : enabledBezelImage);
  284.  
  285.     if (hidden) {
  286.         PSsetgray(0.666); NXRectFill(cellFrame);
  287.         return self;
  288.     }
  289.     PSgsave();
  290.     if (flipped) {
  291.         PSscale(1.0, -1.0);
  292.         PStranslate(0.0, -NX_HEIGHT(cellFrame));
  293.         imFlipped = YES;
  294.     }
  295.     if ((style != MISC_SLIDER_STYLE) &&
  296.             (!(style == MISC_PIECHART_STYLE) && !cFlags1.disabled)) {
  297.         MiscCSDrawBackground(center.x, center.y, radius,
  298.                 NX_HEIGHT(cellFrame), NX_WIDTH(cellFrame));
  299.     }
  300.     if (style == MISC_SLIDER_STYLE)
  301.         [bezel composite:NX_SOVER fromRect:&frame toPoint:&(frame.origin)];
  302.     PSgrestore();
  303.     return self;
  304. }
  305.  
  306. - renderAll
  307. {
  308.     float ang, r4 = radius - _BEZELS_FUDGE;
  309.  
  310.     if ((style != MISC_SLIDER_STYLE) &&
  311.             !((style == MISC_PIECHART_STYLE) && cFlags1.disabled)) {
  312.         MiscCSDrawBackground(center.x, center.y, r4, radius, radius);
  313.     }
  314.     ang = currentangle(value, minValue, maxValue);
  315.     switch (style) {
  316.         case MISC_PIECHART_STYLE : {
  317.             if (cFlags1.disabled) {
  318.                 MiscCSPieChartDisabled(center.x, center.y,
  319.                         r4, radius, radius, ang);
  320.             } else {
  321.                 MiscCSPieChart(center.x, center.y, r4,
  322.                         radius, radius, 90 - ang);
  323.             }
  324.             break;
  325.         }
  326.         case MISC_DIAL_STYLE : {
  327.             MiscCSControlDial(center.x, center.y, r4, radius, radius,
  328.                     ang, (cFlags1.disabled ? 0.333 : 0.0));
  329.             break;
  330.         }
  331.         case MISC_SHUTTLEWHEEL_STYLE : {
  332.             if (cFlags1.disabled) {
  333.                 MiscCSControlKnobDisabled(center.x, center.y,
  334.                         r4, radius, radius);
  335.             } else {
  336.                 MiscCSControlKnob(center.x, center.y, r4, radius, radius, ang);
  337.             }
  338.             break;
  339.         }
  340.         default : break;
  341.     }
  342.     return self;
  343. }
  344.  
  345. - drawKnob:(const NXRect *)knobRect
  346. {
  347.     NXRect frame = { { 0.0, 0.0 }, { radius * 2, radius * 2 } };
  348.     NXPoint position; float ang;
  349.     id bezel = (cFlags1.disabled ? disabledBezelImage : enabledBezelImage);
  350.  
  351.     if (hidden) {
  352.         PSsetgray(0.666); NXRectFill(&_lastFrame);
  353.         return self;
  354.     }
  355.     PSgsave();
  356.     if (imFlipped) {
  357.         PSscale(1.0, -1.0);
  358.         PStranslate(0.0, -NX_HEIGHT(&_lastFrame)); // ***** probably wrong
  359.         
  360.     }
  361.     if ((style != MISC_SLIDER_STYLE) &&
  362.             (!(style == MISC_PIECHART_STYLE) && !cFlags1.disabled)) {
  363.         MiscCSDrawBackground(center.x, center.y, radius - 4,
  364.                 NX_HEIGHT(&_lastFrame), NX_WIDTH(&_lastFrame));
  365.     }
  366.     ang = currentangle(value, minValue, maxValue);
  367.     if  (style != MISC_SLIDER_STYLE) {
  368.         [self renderAll];
  369.     } else {
  370.         [bezel composite:NX_SOVER fromRect:&frame toPoint:&(frame.origin)];
  371.         xycoord(ang, radius - sliderPathWidth / 2 - 2,
  372.                 &(position.x), &(position.y));
  373.         position.x += (center.x - sliderPathWidth
  374.                 * _KNOB_WIDTH_FUDGE + _KNOB_X_FUDGE);
  375.         position.y += (center.y - sliderPathWidth
  376.                 * _KNOB_WIDTH_FUDGE + _KNOB_Y_FUDGE);
  377.         [knobImage composite:NX_SOVER toPoint:&position];
  378.     }
  379.     PSgrestore();
  380.     return self;
  381. }
  382.  
  383. #define _COPYRECT(x, y) NXSetRect((x), \
  384.         NX_X(y), NX_Y(y), NX_WIDTH(y), NX_HEIGHT(y))
  385.  
  386. - drawSelf:(const NXRect *)cellFrame inView:controlView
  387. {
  388.     if ([controlView isFlipped]) imFlipped = YES;
  389.     if (!NXEqualRect(&_lastFrame, cellFrame)) {
  390.         _COPYRECT(&_lastFrame, cellFrame);
  391.         radius = MIN(NX_WIDTH(cellFrame), NX_HEIGHT(cellFrame)) / 2;
  392.         center.x = NX_X(cellFrame) + radius;
  393.         center.y = NX_Y(cellFrame) + radius;
  394.         if (style == MISC_SLIDER_STYLE) [self _cacheImages];
  395.     }
  396.     if (hidden) {
  397.         PSsetgray(0.666); NXRectFill(cellFrame);
  398.         return self;
  399.     }
  400.     if (NXDrawingStatus == NX_PRINTING) {
  401.         // make sure everything gets rendered...
  402.         [self drawBarInside:cellFrame flipped:imFlipped];
  403.     }
  404.     [self drawKnob:cellFrame];
  405.     return self;
  406. }
  407.  
  408. - getKnobRect:(NXRect *)knobRect flipped:(BOOL)flipped
  409. {
  410.     [super getKnobRect:knobRect flipped:flipped];
  411.     return self;
  412. }
  413.  
  414. @end
  415.