# ftp.nice.ch/pub/next/connectivity/protocol/GateKeeper.2.2.s.tar.gz#/GateKeeper.2.2.s/CircularSlider/Source/CircularSlider.subproj/CircularSliderCell.m

```/*
*    Filename:	CircularSliderCell.m
*    Created :	Sun Oct 18 16:56:38 1992
*    Author  :	Vince DeMarco
*		<vince@whatnxt.cuc.ab.ca>
*    LastEditDate was "Wed Feb  3 17:30:28 1993"
*/

#import "CircularSliderCell.h"
#import <appkit/Control.h>
#import <appkit/Application.h>
#import <math.h>
#import <dpsclient/psops.h>
#import "wraps.h"

/* Calculate the Absolute value of y safely */
#define ABS(y)                       \
({typeof (y) __x = (y);        \
(__x < 0) ? (-(__x)) : (__x); })

@implementation CircularSliderCell

/* Given a x,y point on a cart coordinate system this function returns the angle (in degrees) from
* the positive side of the Y- axis
*        ^
*        |
*        |\
*   <____|_|__>
*        | |
*        |<-
*        |
*/

inline static float angle(float x, float y)
{
float result;

if (y >= 0){                                 /* Quadrants 1,4 */
if ( x >= 0){                   /* Quadrant 1 */
result  = atan(ABS(x/y));
result *= (180/M_PI);
return(ABS(result));
result  = atan(ABS(y/x));
result *= (180/M_PI);
return(ABS(result+270.0));
}
if ( x >= 0){                   /* Quadrant 2 */
result  = atan(ABS(y/x));
result *= (180/M_PI);
return(ABS(result+90.0));
result  = atan(ABS(x/y));
result *= (180/M_PI);
return(ABS(result+180.0));
}
}
}

/*
* xycoord does the opposite of angle,
* given an angle and a radius, xycoord returns the x,y position of
* a point on the radius
*        ^
*        |
*        |\
*        | \  angle in degrees
*   <____|__\___>
*        |\  |
*        | \<-
*        |  \ (x,y)
*/
inline static void xycoord(float angle, float radius,float *x, float *y)
{
}

+ (BOOL)prefersTrackingUntilMouseUp
{
return YES;
}

- init
{
self = [super init];
minValue = value = currentang = 0.0;
maxValue = 360.0;
scale_value = 360.0/(ABS(maxValue-minValue));
pieChart = hidden = jumpToMousePoint = NO;
return self;
}

- drawSelf:(const NXRect *)cellFrame inView:controlView
{
center.x = cellFrame->size.width/2;
center.y = cellFrame->size.height/2;
radius = ( center.x < center.y ? center.x : center.y) - 4;
scalex = cellFrame->size.height/ cellFrame->size.width;
scaley = cellFrame->size.width / cellFrame->size.height;

if ([controlView isFlipped]){
imFlipped = YES;
}
if (hidden == YES){
[controlView lockFocus];
/* PSsetgray(0.66666); */
NXSetColor(NX_COLORLTGRAY);
PSrectfill(cellFrame->origin.x,cellFrame->origin.y,cellFrame->size.width,cellFrame->size.height);
[controlView unlockFocus];
return self;
}
[controlView lockFocus];
PSgsave();
if (imFlipped) {
PSscale(1.0, -1.0);
}
if (cFlags1.bezeled == 1){
NXDrawButton(cellFrame,cellFrame);
}else{
if (cFlags1.bordered == 1){
/* PSsetgray(0.0); */
NXSetColor(NX_COLORBLACK);
NXFrameRect(cellFrame);
}
}

if (pieChart == NO){
}else{
if (cFlags1.disabled == 0)
}
PSgrestore();
[controlView unlockFocus];

[self drawInside:cellFrame inView:controlView];
return self;
}

- drawInside:(const NXRect *)cellFrame inView:aView  /* Draws only control portion not bezzel */
{
float x,y;

if (jumpToMousePoint == NO){
currentang += deltaAngle;
if (currentang > 360.0)
currentang -= 360.0;
if (currentang < 0.0)
currentang += 360.0;
}

[aView lockFocus];
PSgsave();
if (imFlipped) {
PSscale(1.0, -1.0);
}

if (pieChart){
if (cFlags1.disabled == 0){
PSPieChart(cellFrame->size.height,cellFrame->size.width,
}else{
PSPieChartDisabled(cellFrame->size.height,cellFrame->size.width,
}
}else{
if (cFlags1.disabled == 0){
}else{
}
}
PSgrestore();
[aView unlockFocus];
return self;
}

- (BOOL)startTrackingAt:(const NXPoint *)startPoint inView:aView
{
return YES;
}

- (BOOL)trackMouse:(NXEvent *)theEvent inRect:(const NXRect *)cellFrame ofView:aView
{
int             looping;                /* Flag for while in modal loop     */
NXPoint         startPoint;             /* Location of mouseDown            */
NXPoint         currentPoint;           /* Location of mouseDragged/mouseUp */
float           newangle   = 0.0;
float           startangle = 0.0;

if ((cFlags1.disabled == 0) && (hidden == NO)){
/* Allow mouseDragged events */

/* Get the location of the mouseDown in view coordinates */
startPoint = theEvent->location;
[aView convertPoint:&startPoint fromView:nil];

if ([self startTrackingAt:&startPoint inView:aView]){

/* Adjust the mouseDown event's location if the Slider has been
* scaled in either the x or y direction
*/

if (scalex > scaley ){
startangle = angle(scalex*(startPoint.x-(center.x)),startPoint.y-(center.y));
}else
if (scalex < scaley ){
startangle = angle(startPoint.x-(center.x),scaley*(startPoint.y-(center.y)));
}else
startangle = angle(startPoint.x-(center.x),startPoint.y-(center.y));

if (jumpToMousePoint == YES){
currentang = startangle;
[self drawInside:cellFrame inView:aView];
[[aView window] flushWindow];
}

/* Run modal loop until mouse up */
looping = YES;
while (looping) {
/* Get the next mouseDragged/mouseUp event */

/* Convert location to view coordinates */
currentPoint = theEvent->location;
[aView convertPoint:&currentPoint fromView:nil];

/* Handle the event */
if (theEvent->type == NX_MOUSEDRAGGED) {

/* Adjust the mouseDown event's location if the Slider has been
* scaled in either the x or y direction
*/

if (scalex > scaley ){
newangle = angle(scalex*(currentPoint.x-(center.x)),currentPoint.y-(center.y));
}else
if (scalex < scaley ){
newangle = angle(currentPoint.x-(center.x),scaley*(currentPoint.y-(center.y)));
}else
if (scalex = scaley ){
newangle = angle(currentPoint.x-(center.x),currentPoint.y-(center.y));
}
if (jumpToMousePoint == NO){
deltaAngle = newangle - startangle;
startangle = newangle;
}else{
currentang = newangle;
}
if (!finite(currentang)){
currentang = 0.0;
}
if (!finite(deltaAngle)){
deltaAngle = 0.0;
}
#if 0
if (currentang != currentang){ /* If this occurs, ie this test is true then
* currentang == NAN */
currentang = 0.0;
}
if (deltaAngle != deltaAngle){ /* If this occurs, ie this test is true then
* deltaAngle == NAN */
deltaAngle = 0.0;
}
#endif
[self drawInside:cellFrame inView:aView];
[[aView window] flushWindow];
if ([self continueTracking:&startPoint at:&currentPoint inView:aView]){
[aView sendAction: action to: target];
}
}else{
looping = NO;
[[aView window] flushWindow];
[aView sendAction: action to: target];
}
startPoint.x = currentPoint.x;
startPoint.y = currentPoint.y;
}
[aView sendAction: action to: target];
}
return YES;
}
return NO;
}

- highlight:(const NXRect *)cellFrame inView:aView lit:(BOOL)flag
{
return self;
}

- (float)floatValue
{
value = (currentang/scale_value) + minValue;

return(value);
}

- (double)doubleValue
{
return((double)[self floatValue]);
}

- (int)intValue
{
return((int)[self floatValue]);
}

{
return self;
}

- setFloatValue:(float)aFloat
{
if (aFloat < minValue){
value = minValue;
currentang = 0;
}else
if (aFloat > maxValue){
value = maxValue;
currentang = 360.0;
}else{
value = aFloat;
currentang = (value - minValue)*scale_value;
}

return self;
}

- setIntValue:(int)aInt
{
[self setFloatValue:(float)aInt];
return self;
}

- (float)minValue
{
return minValue;
}

- setMinValue:(float)aFloat
{
float scale = ABS(aFloat-minValue);
if (scale != 0.0){
minValue = aFloat;
scale_value = 360.0/(ABS(maxValue-minValue));
}
return self;
}

- (float)maxValue
{
return maxValue;
}

- setMaxValue:(float)aFloat
{
float scale = ABS(aFloat-minValue);
if (scale != 0.0){
maxValue = aFloat;
scale_value = 360.0/scale;
}
return self;
}

- setDrawAsPieChart:(BOOL)aBOOL
{
pieChart = aBOOL;
return self;
}

- (BOOL)drawAsPieChart
{
return pieChart;
}

- setJumpToMousePoint:(BOOL)aBOOL
{
jumpToMousePoint = aBOOL;
return self;
}

- (BOOL)jumpToMousePoint
{
return jumpToMousePoint;
}

- setHidden:(BOOL)flag
{
hidden = flag;
return self;
}

- (BOOL)hidden
{
return hidden;
}

{
return self;
}

- write:(NXTypedStream *)stream
{
[super write:stream];
NXWriteTypes(stream,"fff",&value,&maxValue,&minValue);
NXWriteTypes(stream,"ccc",&pieChart,&jumpToMousePoint,&hidden);