|
|
1.1 root 1: /*
2: * GaugeView.m, analog gauge view
3: * Author: Bruce Blumberg, NeXT Developer Support Group.
4: * Originally written for 0.6 mid 1988, modified for 1.0 by Ali Ozer.
5: * Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support
6: *
7: * Subclass of view to implement a simple round analog gauge. You can set the
8: * minimum, maximum value, start angle, angle range, title, font, and more.
9: * It is a pretty generic round gauge view, if you ever have need for one.
10: *
11: * You may freely copy, distribute and reuse the code in this example.
12: * NeXT disclaims any warranty of any kind, expressed or implied, as to
13: * its fitness for any particular use.
14: */
15:
16: #import "GaugeView.h"
17: #import "Gauge.h" // PSwrap routines
18: #import <appkit/Font.h>
19: #import <appkit/Window.h>
20: #import <appkit/Slider.h>
21: #import <soundkit/Sound.h>
22: #import <dpsclient/wraps.h> // PScomposite(), ...
23: #import <math.h> // sin(), cos(), ...
24: #import <string.h> // strcpy(), ...
25:
26:
27: @implementation GaugeView : View
28:
29:
30: #define HANDRATIO 0.65 /* hand length compared face size */
31:
32: - initFrame:(NXRect *)frameRect;
33: /* Basic initFrame method which just calls the more complicated
34: * initFrame:min:max... method with some generally good default values.
35: */
36: {
37: [self initFrame:frameRect min:0.0 max:100.0
38: startAngle:215.0 range:250.0 tickInterval:10];
39: return self;
40: }
41:
42: - initFrame:(NXRect *)frameRect min:(float)min max:(float)max
43: startAngle:(float)start range:(float)range tickInterval:(int)interval
44: /* Init method for newly created analog gauge. It creates the face of the
45: * gauge in an offscreen window called cacheWindow when drawSelf:: is called
46: * the image is composited into the view and the hand is drawn on top of the
47: * face.
48: */
49: {
50: [super initFrame:frameRect];
51: /* Create offscreen window for face. Note that the defer: argument
52: * has to be NO for windows which will remain offscreen. Deferred
53: * windows only get created on a orderFront: (or any other method
54: * that causes them to come on screen).
55: */
56: cacheWindow = [[Window allocFromZone:[self zone]]
57: initContent:frameRect
58: style:NX_PLAINSTYLE
59: backing:NX_RETAINED
60: buttonMask:0
61: defer:NO];
62: startAngle = start;
63: angleRange = range;
64: tickInterval = interval;
65: value = min;
66: [self setMin:min];
67: [self setMax:max];
68: [self setFont:"Helvetica" size:10.0];
69: [self setTitle:"Stress"];
70: center.x = bounds.size.width/2.0;
71: center.y = bounds.size.height/2.0;
72: radius = (bounds.size.height/2.0) - 8.0;
73:
74: /* This pswrap creates a PS function which is used to draw the hand. */
75: PSWmakeHand(radius*HANDRATIO);
76: [self drawFace];
77: return self;
78: }
79:
80: - free;
81: /* Free cacheWindow, too!
82: */
83: {
84: [cacheWindow free];
85: return [super free];
86: }
87:
88:
89: /* SET PARAMETERS */
90:
91: - setFont:(char *)fName size:(float)fSize
92: /* Sets font used for drawing title and numbers. Change is displayed
93: * next time drawSelf:: is executed. needRedraw is set to indicate that
94: * face image must be redrawn.
95: */
96: {
97: font = [Font newFont:fName size:fSize style:0 matrix:NX_IDENTITYMATRIX];
98: needRedraw = YES;
99: return self;
100: }
101:
102: - setTitle:(char *)str;
103: /* Sets title of gauge. needRedraw is set to indicate that
104: * face image must be redrawn.
105: */
106: {
107: strcpy(title,str);
108: needRedraw = YES;
109: return self;
110: }
111:
112: - setMin:(float)newValue;
113: /* Sets minimum for gauge, recalculates degreesPerUnit which is used to
114: * determine interval of ticks and labels. needRedraw is set to indicate that
115: * face image must be redrawn.
116: */
117: {
118: minValue = newValue;
119: degreesPerUnit = angleRange/(maxValue-minValue);
120: needRedraw = YES;
121: return self;
122: }
123:
124: - setMax:(float)newValue;
125: /* Sets maximum for gauge, recalculates degreesPerUnit which is used to
126: * determine interval of ticks and labels. needRedraw is set to indicate that
127: * face image must be redrawn.
128: */
129: {
130: maxValue = newValue;
131: degreesPerUnit = angleRange/(maxValue-minValue);
132: needRedraw = YES;
133: return self;
134: }
135:
136: - setStartAngle:(float)newValue;
137: /* Sets start angle for gauge, which is the angle of the arm when at the
138: * minimum value. needRedraw is set to indicate that face image must be
139: * redrawn.
140: */
141: {
142: startAngle = newValue;
143: needRedraw = YES;
144: return self;
145: }
146:
147: - setAngleRange:(float)newValue;
148: /* Sets angle range for gauge, which is the sweep of the arm from minimum
149: * value to maximum value. The value cannot exceed 360 degrees (a full
150: * revolution). Recalculates degreesPerUnit which is used to
151: * determine interval of ticks and labels. needRedraw is set to indicate that
152: * face image must be redrawn.
153: */
154: {
155: if (newValue > 360) newValue = 360.0;
156: angleRange = newValue;
157: degreesPerUnit = angleRange/(maxValue-minValue);
158: needRedraw = YES;
159: return self;
160: }
161:
162: - setTickInterval:(int)newValue;
163: /* Sets tick interval for gauge, which is number of units between tick
164: * marks on gauge face. needRedraw is set to indicate that
165: * face image must be redrawn.
166: */
167: {
168: tickInterval = newValue;
169: needRedraw = YES;
170: return self;
171: }
172:
173:
174: - setValueFormCell:anObject;
175: {
176: valueFormCell = anObject;
177: [valueFormCell setFloatingPointFormat:NO left:3 right:0];
178: return self;
179: }
180:
181: - (BOOL)textWillEnd:textObject;
182: /* Rejects entry into the form cell if it isn't in the range of the
183: * min and max value for the gauge.
184: */
185: {
186: id cell;
187: float newValue;
188:
189: cell = [textObject delegate];
190: newValue = [cell floatValue];
191: return (newValue > maxValue) || (newValue < minValue);
192: }
193:
194: /* TARGET/ACTION METHODS */
195:
196: - changeValue:sender
197: /* Target/Action for a IB control. Takes floatValue from control (could be
198: * slider or field), makes sure slider and field are in sync, displays arm
199: * at new value.
200: */
201: { float newValue;
202:
203: newValue = [sender floatValue];
204: if (newValue != value) { // if value changed
205: value = newValue;
206: if (sender == valueSlider)
207: [valueFormCell setFloatValue:newValue];
208: else
209: [valueSlider setFloatValue:newValue];
210: [self display];
211: if (value == maxValue) // if at maximum value
212: [[Sound findSoundFor:"HighStress"] play];
213: }
214: return self;
215: }
216:
217:
218: /* PRIVATE METHODS */
219:
220: - drawFace
221: /* drawFace draws the gauge face image in the offscreen window. It erases
222: * the background, draws the circular border, displays the gauge title,
223: * draws the tick marks and labels them appropriately. The offscreen cache
224: * is composited on screen and then the hand is drawn on top of the face
225: * for the current gauge value.
226: */
227: {
228: float angle, angleIncrement;
229: int number;
230: NXSize string;
231: NXPoint pt;
232: char numString[10];
233:
234: [[cacheWindow contentView] lockFocus];
235:
236: PSsetgray(NX_LTGRAY);
237: NXRectFill(&bounds);
238: PSWdrawBorder(center.x, center.y, radius);
239: angleIncrement = angleRange/((maxValue-minValue)/tickInterval);
240: PSWdrawTicks(center.x, center.y, radius*HANDRATIO,
241: angleIncrement/2, startAngle, startAngle+angleRange);
242: [font set];
243: string.height = [font pointSize];
244: string.width = [font getWidthOf:title];
245: PSWdrawString((bounds.size.width-string.width)/2, center.y+8, title);
246:
247: number = minValue;
248: for(angle=startAngle;angle>=startAngle-angleRange;angle -= angleIncrement){
249: sprintf(numString,"%d",number);
250: string.width = [font getWidthOf:numString];
251: pt.x = cos(angle/57.3)*(radius-1.0-string.width/2)+ center.x;
252: pt.y = sin(angle/57.3)*(radius-1.0-string.height/2) + center.y ;
253: PSWdrawString(pt.x-(string.width/2), pt.y-(string.height/2.5),
254: numString);
255: number += tickInterval;
256: }
257:
258: [[cacheWindow contentView] unlockFocus];
259: needRedraw = NO;
260: return self;
261: }
262:
263:
264: - drawHand
265: /* Calculates the angle for the current value and draws the hand there.
266: */
267: {
268: float valueAngle;
269:
270: valueAngle = startAngle - degreesPerUnit*(value-minValue);
271: PSWdrawHand(center.x,center.y,valueAngle);
272: return self;
273: }
274:
275: - drawSelf:(NXRect *)drawRects :(int)rectCount
276: /* Draws face and hand of gauge. If needRedraw is YES, a parameter has
277: * changed and the face must be redrawn in the offscreen window.
278: * Otherwise, the image in cacheWindow is copied into the bounds of the
279: * view, and a PSWrap is called which draws the hand in the
280: * correct position.
281: */
282: {
283:
284: if (needRedraw)
285: [self drawFace];
286: PScomposite(0.0, 0.0, bounds.size.width, bounds.size.height,
287: [cacheWindow gState], 0.0, 0.0, NX_COPY);
288: [self drawHand];
289: return self;
290: }
291:
292: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.