home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.0
/
NeXTSTEP3.0.iso
/
NextDeveloper
/
Examples
/
AppKit
/
BusyBox
/
GaugeView.m
< prev
next >
Wrap
Text File
|
1990-10-26
|
8KB
|
292 lines
/*
* 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 ti(6terval:(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 ti(7and 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(8f;
}
/* 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