This is PAThumbWheelCell.m in view mode; [Download] [Up]
#import "PAThumbWheelCell.h" #import "PAThumbWheelCellDrawing.h" #import "PAThumbWheel.h" /****************************************************************************** PAThumbWheelCell PAThumbWheel offers the functionality of Slider plus the features that you would expect from a real thumbwheel (including 2 3/4 D Graphics!). PAThumbWheel has a linear display mode and a radial display mode and offers the ability to assign a value to the visible region of the control as well as an absolute value that the ThumbWheel will either ignore, bound to or wrap around. PAThumbWheel can also return relative values via its -relativeIntValue & -relativeFloatValue methods. A snap back option allows mouse loops to start from and return to a base value. Copyright 1992, Jeff Martin. (jmartin@next.com 415-780-3833) ******************************************************************************/ #define PI 3.141592654 #define HALF_PI 1.570796327 #define DEG_TO_RAD 0.0174532925199433 // PI/180.0 #define SIN(x) (sin(DEG_TO_RAD*(x))) #define COS(x) (cos(DEG_TO_RAD*(x))) #define TAN(x) (tan(DEG_TO_RAD*(x))) #define RAD_TO_DEG 57.295779513082320876 // 180.0/PI #define ASIN(x) (asin(x)*RAD_TO_DEG) #define ACOS(x) (acos(x)*RAD_TO_DEG) #define ATAN(x) (atan(x)*RAD_TO_DEG) // A mod function for floating values #define MOD(x,y) ((x) - (y)*(int)((float)(x)/(y))) // Keep 'a' between x and y #define CLAMP(a,x,y) (MAX((x), MIN((y), (a)))) // Keep 'a' between 'x' and 'y' by wrapping it to the other side #define CLAMP_WITH_WRAP(a,x,y) \ ( ((a) < (x)) ? ((y) - MOD(((x)-(a)),((y)-(x)))) : \ ( ((a) > (y)) ? ((x) + MOD(((a)-(y)),((y)-(x)))) : (a) ) ) // Is a between x and y #define ISBETWEEN(a,x,y) (((a)>=(x))&&((a)<=(y))) #define EQUAL(a,b) (ABS((a)-(b))<0.00001) @implementation PAThumbWheelCell // Set reasonable defaults - init { [super init]; [self setType:NX_TEXTCELL]; [self setHorizontal]; [self setLinear]; [self setUnbounded]; [self setVisibleMax:1.0]; [self setVisibleMin:-1.0]; [self setAbsoluteMax:2.0]; [self setAbsoluteMin:-2.0]; [self setSnapsBack:YES]; [self setSnapBackValue:0.0]; [self setDashInterval:10]; [self setShowMainDash:YES]; [self setColor:NX_COLORLTGRAY]; [self sendActionOn:NX_MOUSEDOWNMASK|NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK]; return self; } // Override trackMouse to provide an infinitely large area(continuous tracking) - (BOOL)trackMouse:(NXEvent *)event inRect:(const NXRect *)rect ofView:view { cellFrame = *rect; return [super trackMouse:event inRect:NULL ofView:view]; } // Override to return YES (always track mouse) - (BOOL)startTrackingAt:(const NXPoint *)startPoint inView:aView { return YES;} // Override from cell to track mouse - (BOOL)continueTracking:(const NXPoint *)lastPoint at:(const NXPoint *)currPoint inView:view { float currPointVal =[self floatValueAtPoint:*currPoint forFrame:cellFrame]; float lastPointVal =[self floatValueAtPoint:*lastPoint forFrame:cellFrame]; float mouseScale = ([NXApp currentEvent]->flags&&NX_ALTERNATEMASK)? .1 : 1; // If TW is bounded and at absolute bound, only track when mouse comes back if([self isBounded]) { // Return if we are already at absoluteMax and currPoint is greater if(EQUAL([self floatValue], [self absoluteMax])) { if(currPointVal > [self absoluteMax]) return YES; else lastPointVal = [self absoluteMax]; } // Return if we are already at absoluteMin and currPoint is less if(EQUAL([self floatValue], [self absoluteMin])) { if(currPointVal < [self absoluteMin]) return YES; else lastPointVal = [self absoluteMin]; } } // Set the float value relative to last point [self setFloatValue:[self floatValue] + (currPointVal - lastPointVal)*mouseScale]; // Redraw control [view display]; return YES; // Always continue to track mouse } /****************************************************************************** floatValueAtPoint:forFrame: This method gives the value that corresponds to a point with respect to the given frame and the visible range. When in radial mode, the point on the thumbwheel is approximated with a power series for arcCos to get legal values for points outside of the frame. ******************************************************************************/ - (float)floatValueAtPoint:(NXPoint)point forFrame:(NXRect)frame { float pos = [self isVertical] ? point.y : point.x; float base = [self isVertical] ? NX_Y(&frame) : NX_X(&frame); float width = [self isVertical] ? NX_HEIGHT(&frame) : NX_WIDTH(&frame); float value; if([self isLinear]) value = [self visibleMin] + (pos - base)/width*[self visibleRange]; else { float radius = width/2; float midP = base + radius; float x = (midP - pos)/radius; // Get degrees by pwr series approximation of ArcCos (Pi/2 - x - x^3/6) float alpha = (HALF_PI - x - x*x*x/6); // Convert degrees to TW coords value = [self visibleMin] + alpha/PI*[self visibleRange]; } return value; } // Override from cell to snapback if neccessary - stopTracking:(const NXPoint *)lastPoint at:(const NXPoint *)stopPoint inView:view mouseIsUp:(BOOL)flag { // If the ThumbWheel is in snap back mode, snap it back and redraw if([self snapsBack]) { [self setFloatValue:[self snapBackValue]]; [self resetRelativeValue]; [view display]; } return [super stopTracking:lastPoint at:stopPoint inView:view mouseIsUp:flag]; } // Direction is either DIRECTION_HORIZONTAL or DIRECTION_VERTICAL - (int)direction { return direction; } - setDirection:(int)dir { direction = dir; [image free]; image = NULL; return self; } - (BOOL)isVertical { return direction == DIRECTION_VERTICAL; } - setVertical { direction = DIRECTION_VERTICAL; return self; } - (BOOL)isHorizontal { return direction == DIRECTION_HORIZONTAL; } - setHorizontal { direction = DIRECTION_HORIZONTAL; return self; } /****************************************************************************** displayMode, setDisplayMode The displayMode is either DISPLAY_MODE_LINEAR or DISPLAY_MODE_RADIAL. Linear displays a flat ruler type control whereas radial displays a 3D thumbwheel that actually looks curved . ******************************************************************************/ - (int)displayMode { return displayMode; } - setDisplayMode:(int)mode { displayMode = mode; return self; } - (BOOL)isRadial { return displayMode == DISPLAY_MODE_RADIAL; } - setRadial { return [self setDisplayMode:DISPLAY_MODE_RADIAL]; } - (BOOL)isLinear { return displayMode == DISPLAY_MODE_LINEAR; } - setLinear { return [self setDisplayMode:DISPLAY_MODE_LINEAR]; } /****************************************************************************** intValue, setIntValue, floatValue, setFloatValue These methods are overridden to allow us to calculate relative values and to constrain the value with respect to the absolute mode and absolute values. ******************************************************************************/ - (int)intValue { return (int)[self floatValue]; } - setIntValue:(int)val { [self setFloatValue:val]; return self; } - (float)floatValue { return floatValue; } - setFloatValue:(float)val { // Clamp or Wrap newValue wrt the absoluteMode if(!ISBETWEEN(val, [self absoluteMin], [self absoluteMax])) { if([self isBounded]) val = CLAMP(val, [self absoluteMin], [self absoluteMax]); else if([self isWrapped]) val = CLAMP_WITH_WRAP(val, [self absoluteMin], [self absoluteMax]); } // Store last float value and set float value lastFloatValue = floatValue; floatValue = val; return self; } - (float)lastFloatValue { return lastFloatValue; } - setLastFloatValue:(float)value { lastFloatValue=value; return self;} /****************************************************************************** visibleMax, visibleMin, visibleRange, middleValue VisibleMax and visibleMin are the values of the thumbwheel at either end; max value is at right/top, min is at left/bottom. middleValue is the value of the TW at the center (wrt visibleMin and visibleMax). ******************************************************************************/ - (float)visibleMax { return visMax; } - setVisibleMax:(float)max { visMax = max; return self; } - (float)visibleMin { return visMin; } - setVisibleMin:(float)min { visMin = min; return self; } - (float)visibleRange { return [self visibleMax] - [self visibleMin]; } - (float)middleValue { return [self visibleMin] + [self visibleRange]/2; } /****************************************************************************** absoluteMode, absoluteMax, absoluteMin The absolute mode refers to ThumbWheel values that exceed the visible range. ABSOLUTE_UNBOUNDED means that the TW can be dragged as high or low as desired. ABSOLUTE_BOUNDED means that the TW will be clamped to some arbitrarily large value. ABSOLUTE_WRAPPED means that the TW will wrap from the absoluteMax to the absoluteMin (and vise-versa) when applicable. AbsoluteMax is the value off to the right and up. AbsoluteMin us the value off to the left and down. ******************************************************************************/ - (int)absoluteMode { return absMode; } - setAbsoluteMode:(int)mode { absMode = mode; return self; } - (BOOL)isUnbounded { return absMode == ABSOLUTE_UNBOUNDED; } - setUnbounded { return [self setAbsoluteMode:ABSOLUTE_UNBOUNDED]; } - (BOOL)isBounded { return absMode == ABSOLUTE_BOUNDED; } - setBounded { return [self setAbsoluteMode:ABSOLUTE_BOUNDED]; } - (BOOL)isWrapped { return absMode == ABSOLUTE_WRAPPED; } - setWrapped { return [self setAbsoluteMode:ABSOLUTE_WRAPPED]; } - (float)absoluteMax { return absMax; } - setAbsoluteMax:(float)value { absMax = value; return self; } - (float)absoluteMin { return absMin; } - setAbsoluteMin:(float)value { absMin = value; return self; } - (float)absoluteRange { return [self absoluteMax] - [self absoluteMin]; } /****************************************************************************** relativeIntValue, relativeFloatValue, resetRelativeValue These two methods return the change of the value since the last iteration. This is useful for a relative method call ( rotateBy: as opposed to rotateTo:). resetRelativeValue sets the relative change to zero (typically only called internally when snapping back). ******************************************************************************/ - (int)relativeIntValue { return [self intValue] - (int)[self lastFloatValue];} - (float)relativeFloatValue { return [self floatValue] - [self lastFloatValue]; } - resetRelativeValue { [self setLastFloatValue:[self floatValue]];return self;} /****************************************************************************** snapsBack, snapBackValue It is sometimes useful to have the thumbwheel snap back to some value (zero by default) so that it can be used for relative modification of values (rotate by as opposed to rotateTo:). ******************************************************************************/ - (BOOL)snapsBack { return snapsBack; } - setSnapsBack:(BOOL)flag { snapsBack = flag; return self; } - (float)snapBackValue {return snapBackValue; } - setSnapBackValue:(float)value { snapBackValue = value;return self;} /****************************************************************************** dashInterval, setDashInterval, setDashIntervalEqual The dash interval is either in degrees (DISPLAY_MODE_RADIAL) or PostScript points (DISPLAY_MODE_LINEAR). The default is 10 of each. ******************************************************************************/ - (float)dashInterval { return dashInterval; } - setDashInterval:(float)val { dashInterval = val; return self; } /****************************************************************************** showMainDash, setShowMainDash The main dash is the dash in the center of the control and gives feedback as to the absolute value of the control. This should be set to NO for TW that only provide relative values. ******************************************************************************/ - (BOOL)showMainDash {return showMainDash;} - setShowMainDash:(BOOL)flag { showMainDash = flag; return self; } /****************************************************************************** color, setColor These methods manipulate the predominant color of the ThumbWheel. Radial ThumbWheel are of course shades of these. The default is NX_COLORLTGRAY. ******************************************************************************/ - (NXColor)color {return color;} - setColor:(NXColor)c { color = c; return self; } /****************************************************************************** shift The shift is how much the dashes are shifted by to achieve the animation of motion it is in points. It is calculated from the visibleRange and the physicalRange (frame). ******************************************************************************/ - (int)shift:(const NXRect *)frame { if([self isLinear]) { if([self isHorizontal]) return (int)(([self floatValue] - [self visibleMin]) / [self visibleRange]*NX_WIDTH(frame) + .5); else return (int)(([self floatValue] - [self visibleMin]) / [self visibleRange]*NX_HEIGHT(frame) +.5); } else return (int)(([self floatValue] - [self visibleMin])/ [self visibleRange]*180 + .5); } // Override highlight:inView:lit: so that it does nothing - highlight:(const NXRect *)frame inView:view lit:(BOOL)flag { return self; } // Override this so that we track mouse whether or not it is on top of us. + (BOOL)prefersTrackingUntilMouseUp { return YES; } /****************************************************************************** The Read and Write Methods are for archival. ******************************************************************************/ - write:(NXTypedStream *)stream { [super write:stream]; NXWriteType(stream, "i", &displayMode); NXWriteType(stream, "i", &direction); NXWriteType(stream, "f", &floatValue); NXWriteType(stream, "f", &lastFloatValue); NXWriteType(stream, "f", &visMax); NXWriteType(stream, "f", &visMin); NXWriteType(stream, "i", &absMode); NXWriteType(stream, "f", &absMax); NXWriteType(stream, "f", &absMin); NXWriteType(stream, "c", &snapsBack); NXWriteType(stream, "f", &snapBackValue); NXWriteType(stream, "i", &dashInterval); NXWriteType(stream, "c", &showMainDash); NXWriteColor(stream, color); return self; } - read:(NXTypedStream *)stream { [super read:stream]; NXReadType(stream, "i", &displayMode); NXReadType(stream, "i", &direction); NXReadType(stream, "f", &floatValue); NXReadType(stream, "f", &lastFloatValue); NXReadType(stream, "f", &visMax); NXReadType(stream, "f", &visMin); NXReadType(stream, "i", &absMode); NXReadType(stream, "f", &absMax); NXReadType(stream, "f", &absMin); NXReadType(stream, "c", &snapsBack); NXReadType(stream, "f", &snapBackValue); NXReadType(stream, "i", &dashInterval); NXReadType(stream, "c", &showMainDash); color = NXReadColor(stream); return self; } - free { [image free]; return [super free]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.