Annotation of Examples/AppKit/BusyBox/GaugeView.m, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.