home *** CD-ROM | disk | FTP | other *** search
- /*
- * GaugeView.m, analog gauge view
- * Author: Bruce Blumberg, NeXT Developer Support Group.
- * Originally written for 0.6 mid 1988, modified for 1.0 by Ali Ozer.
- * Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support
- *
- * Subclass of view to implement a simple round analog gauge. You can set the
- * minimum, maximum value, start angle, angle range, title, font, and more.
- * It is a pretty generic round gauge view, if you ever have need for one.
- *
- * You may freely copy, distribute and reuse the code in this example.
- * NeXT disclaims any warranty of any kind, expressed or implied, as to
- * its fitness for any particular use.
- */
-
- #import "GaugeView.h"
- #import "Gauge.h" // PSwrap routines
- #import <appkit/Font.h>
- #import <appkit/Window.h>
- #import <appkit/Slider.h>
- #import <soundkit/Sound.h>
- #import <dpsclient/wraps.h> // PScomposite(), ...
- #import <math.h> // sin(), cos(), ...
- #import <string.h> // strcpy(), ...
-
-
- @implementation GaugeView : View
-
-
- #define HANDRATIO 0.65 /* hand length compared face size */
-
- - initFrame:(NXRect *)frameRect;
- /* Basic initFrame method which just calls the more complicated
- * initFrame:min:max... method with some generally good default values.
- */
- {
- [self initFrame:frameRect min:0.0 max:100.0
- startAngle:215.0 range:250.0 tickInterval:10];
- return self;
- }
-
- - initFrame:(NXRect *)frameRect min:(float)min max:(float)max
- startAngle:(float)start range:(float)range tickInterval:(int)interval
- /* Init method for newly created analog gauge. It creates the face of the
- * gauge in an offscreen window called cacheWindow when drawSelf:: is called
- * the image is composited into the view and the hand is drawn on top of the
- * face.
- */
- {
- [super initFrame:frameRect];
- /* Create offscreen window for face. Note that the defer: argument
- * has to be NO for windows which will remain offscreen. Deferred
- * windows only get created on a orderFront: (or any other method
- * that causes them to come on screen).
- */
- cacheWindow = [[Window allocFromZone:[self zone]]
- initContent:frameRect
- style:NX_PLAINSTYLE
- backing:NX_RETAINED
- buttonMask:0
- defer:NO];
- startAngle = start;
- angleRange = range;
- tickInterval = interval;
- value = min;
- [self setMin:min];
- [self setMax:max];
- [self setFont:"Helvetica" size:10.0];
- [self setTitle:"Stress"];
- center.x = bounds.size.width/2.0;
- center.y = bounds.size.height/2.0;
- radius = (bounds.size.height/2.0) - 8.0;
-
- /* This pswrap creates a PS function which is used to draw the hand. */
- PSWmakeHand(radius*HANDRATIO);
- [self drawFace];
- return self;
- }
-
- - free;
- /* Free cacheWindow, too!
- */
- {
- [cacheWindow free];
- return [super free];
- }
-
-
- /* SET PARAMETERS */
-
- - setFont:(char *)fName size:(float)fSize
- /* Sets font used for drawing title and numbers. Change is displayed
- * next time drawSelf:: is executed. needRedraw is set to indicate that
- * face image must be redrawn.
- */
- {
- font = [Font newFont:fName size:fSize style:0 matrix:NX_IDENTITYMATRIX];
- needRedraw = YES;
- return self;
- }
-
- - setTitle:(char *)str;
- /* Sets title of gauge. needRedraw is set to indicate that
- * face image must be redrawn.
- */
- {
- strcpy(title,str);
- needRedraw = YES;
- return self;
- }
-
- - setMin:(float)newValue;
- /* Sets minimum for gauge, recalculates degreesPerUnit which is used to
- * determine interval of ticks and labels. needRedraw is set to indicate that
- * face image must be redrawn.
- */
- {
- minValue = newValue;
- degreesPerUnit = angleRange/(maxValue-minValue);
- needRedraw = YES;
- return self;
- }
-
- - setMax:(float)newValue;
- /* Sets maximum for gauge, recalculates degreesPerUnit which is used to
- * determine interval of ticks and labels. needRedraw is set to indicate that
- * face image must be redrawn.
- */
- {
- maxValue = newValue;
- degreesPerUnit = angleRange/(maxValue-minValue);
- needRedraw = YES;
- return self;
- }
-
- - setStartAngle:(float)newValue;
- /* Sets start angle for gauge, which is the angle of the arm when at the
- * minimum value. needRedraw is set to indicate that face image must be
- * redrawn.
- */
- {
- startAngle = newValue;
- needRedraw = YES;
- return self;
- }
-
- - setAngleRange:(float)newValue;
- /* Sets angle range for gauge, which is the sweep of the arm from minimum
- * value to maximum value. The value cannot exceed 360 degrees (a full
- * revolution). Recalculates degreesPerUnit which is used to
- * determine interval of ticks and labels. needRedraw is set to indicate that
- * face image must be redrawn.
- */
- {
- if (newValue > 360) newValue = 360.0;
- angleRange = newValue;
- degreesPerUnit = angleRange/(maxValue-minValue);
- needRedraw = YES;
- return self;
- }
-
- - setTickInterval:(int)newValue;
- /* Sets tick interval for gauge, which is number of units between tick
- * marks on gauge face. needRedraw is set to indicate that
- * face image must be redrawn.
- */
- {
- tickInterval = newValue;
- needRedraw = YES;
- return self;
- }
-
-
- - setValueFormCell:anObject;
- {
- valueFormCell = anObject;
- [valueFormCell setFloatingPointFormat:NO left:3 right:0];
- return self;
- }
-
- - (BOOL)textWillEnd:textObject;
- /* Rejects entry into the form cell if it isn't in the range of the
- * min and max value for the gauge.
- */
- {
- id cell;
- float newValue;
-
- cell = [textObject delegate];
- newValue = [cell floatValue];
- return (newValue > maxValue) || (newValue < minValue);
- }
-
- /* TARGET/ACTION METHODS */
-
- - changeValue:sender
- /* Target/Action for a IB control. Takes floatValue from control (could be
- * slider or field), makes sure slider and field are in sync, displays arm
- * at new value.
- */
- { float newValue;
-
- newValue = [sender floatValue];
- if (newValue != value) { // if value changed
- value = newValue;
- if (sender == valueSlider)
- [valueFormCell setFloatValue:newValue];
- else
- [valueSlider setFloatValue:newValue];
- [self display];
- if (value == maxValue) // if at maximum value
- [[Sound findSoundFor:"HighStress"] play];
- }
- return self;
- }
-
-
- /* PRIVATE METHODS */
-
- - drawFace
- /* drawFace draws the gauge face image in the offscreen window. It erases
- * the background, draws the circular border, displays the gauge title,
- * draws the tick marks and labels them appropriately. The offscreen cache
- * is composited on screen and then the hand is drawn on top of the face
- * for the current gauge value.
- */
- {
- float angle, angleIncrement;
- int number;
- NXSize string;
- NXPoint pt;
- char numString[10];
-
- [[cacheWindow contentView] lockFocus];
-
- PSsetgray(NX_LTGRAY);
- NXRectFill(&bounds);
- PSWdrawBorder(center.x, center.y, radius);
- angleIncrement = angleRange/((maxValue-minValue)/tickInterval);
- PSWdrawTicks(center.x, center.y, radius*HANDRATIO,
- angleIncrement/2, startAngle, startAngle+angleRange);
- [font set];
- string.height = [font pointSize];
- string.width = [font getWidthOf:title];
- PSWdrawString((bounds.size.width-string.width)/2, center.y+8, title);
-
- number = minValue;
- for(angle=startAngle;angle>=startAngle-angleRange;angle -= angleIncrement){
- sprintf(numString,"%d",number);
- string.width = [font getWidthOf:numString];
- pt.x = cos(angle/57.3)*(radius-1.0-string.width/2)+ center.x;
- pt.y = sin(angle/57.3)*(radius-1.0-string.height/2) + center.y ;
- PSWdrawString(pt.x-(string.width/2), pt.y-(string.height/2.5),
- numString);
- number += tickInterval;
- }
-
- [[cacheWindow contentView] unlockFocus];
- needRedraw = NO;
- return self;
- }
-
-
- - drawHand
- /* Calculates the angle for the current value and draws the hand there.
- */
- {
- float valueAngle;
-
- valueAngle = startAngle - degreesPerUnit*(value-minValue);
- PSWdrawHand(center.x,center.y,valueAngle);
- return self;
- }
-
- - drawSelf:(NXRect *)drawRects :(int)rectCount
- /* Draws face and hand of gauge. If needRedraw is YES, a parameter has
- * changed and the face must be redrawn in the offscreen window.
- * Otherwise, the image in cacheWindow is copied into the bounds of the
- * view, and a PSWrap is called which draws the hand in the
- * correct position.
- */
- {
-
- if (needRedraw)
- [self drawFace];
- PScomposite(0.0, 0.0, bounds.size.width, bounds.size.height,
- [cacheWindow gState], 0.0, 0.0, NX_COPY);
- [self drawHand];
- return self;
- }
-
- @end