|
|
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.