|
|
1.1 ! root 1: /* ! 2: * ClockView.m, a simple clock view ! 3: * Author: Ali T. Ozer, NeXT Developer Support Group ! 4: * Created: May 26, 1989 (for version 0.9) ! 5: * Modified: June 14 and Aug 14, 1989 (for version 1.0) ! 6: * Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support ! 7: * Modified some for 3.0 by Ali Ozer, AppKit Group, May 28, 1992 ! 8: * ! 9: * Subclass of view to implement a simple clock. This view is pretty generic ! 10: * and can probably be added to any program. The setClockType: method lets ! 11: * you display an analog, digital, or sundial face. You have the option of ! 12: * turning the seconds hand on or off, as well as controlling whether the date ! 13: * is also displayed. ! 14: * ! 15: * You may freely copy, distribute and reuse the code in this example. ! 16: * NeXT disclaims any warranty of any kind, expressed or implied, as to its ! 17: * fitness for any particular use. ! 18: */ ! 19: ! 20: #import <appkit/appkit.h> ! 21: #import "ClockView.h" ! 22: #import "Clock.h" // PSwrap routines ! 23: #import <objc/NXStringTable.h> ! 24: #import <string.h> ! 25: #import <sys/time.h> ! 26: ! 27: ! 28: @implementation ClockView:View ! 29: ! 30: ! 31: #define PI (double)3.1415926535897 ! 32: ! 33: #define ANALOG 0 ! 34: #define DIGITAL 1 ! 35: #define SUNDIAL 2 ! 36: ! 37: ! 38: ! 39: /* ShowTime() is the timed entry function called by the ! 40: * timed entry mechanism. It first writes the time out on the face ! 41: * of the clock, and then reinstalls the timed entry if the clock is ! 42: * not showing the seconds. If the seconds are being shown, no need to ! 43: * reinstall the timed entry; a second skipped here and then won't matter. ! 44: * If minutes are being shown, we want to make sure that the minute jumps ! 45: * at the next top of the minute, regardless of how long it took to service ! 46: * the timed entry. ! 47: */ ! 48: void ShowTime (teNum, now, clock) ! 49: DPSTimedEntry teNum; ! 50: double now; ! 51: id clock; ! 52: { ! 53: [clock display]; ! 54: if ([clock showSeconds] == NO) [[clock stopTimedEntry] startTimedEntry:NO]; ! 55: } ! 56: ! 57: ! 58: ! 59: ! 60: - initFrame:(const NXRect *)frameRect ! 61: /* initFrame for newly created view, initializes the various parameters, ! 62: * grabs some fonts to be used later. ! 63: */ ! 64: { ! 65: [super initFrame:frameRect]; ! 66: ! 67: face = [[NXImage allocFromZone:[self zone]] initSize:&bounds.size]; ! 68: [face useDrawMethod:@selector(drawFace:) inObject:self]; ! 69: ! 70: littleFont = [Font newFont:"Helvetica" size:12 style:0 ! 71: matrix:NX_IDENTITYMATRIX]; ! 72: mediumFont = [Font newFont:"Times-Roman" size:14 style:0 ! 73: matrix:NX_IDENTITYMATRIX]; ! 74: bigFont = [Font newFont:"Times-Roman" size:24 style:0 ! 75: matrix:NX_IDENTITYMATRIX]; ! 76: ! 77: /* Set the default state (analog face, no seconds, date on) */ ! 78: clockType = ANALOG; ! 79: showSeconds = NO; ! 80: showDate = YES; ! 81: center.x = bounds.size.width/2.0; ! 82: center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0; ! 83: radius = MIN(center.x,center.y-[mediumFont pointSize]); ! 84: [face recache]; ! 85: ! 86: /* Start the time entry. YES indicates that this is the first time */ ! 87: [self startTimedEntry:YES]; ! 88: [self display]; ! 89: return self; ! 90: } ! 91: ! 92: ! 93: - free ! 94: /* Good idea to get rid of the timed entry while freeing... ! 95: */ ! 96: { ! 97: [face free]; ! 98: [self stopTimedEntry]; ! 99: return [super free]; ! 100: } ! 101: ! 102: ! 103: /* SET/GET CLOCK PARAMETERS */ ! 104: ! 105: - setShowSeconds:(BOOL)newValue ! 106: /* setShowSeconds: sets whether or not the seconds hand is shown. ! 107: * The timed entry must be reinstalled whenever this setting is changed ! 108: * as time to the next firing changes. ! 109: */ ! 110: { ! 111: showSeconds = newValue; ! 112: [[self stopTimedEntry] startTimedEntry:NO]; ! 113: [self display]; ! 114: return self; ! 115: } ! 116: ! 117: - setShowDate:(BOOL)newValue ! 118: /* setShowDate: sets whether or not the date is shown. ! 119: */ ! 120: { ! 121: showDate = newValue; ! 122: [self display]; ! 123: return self; ! 124: } ! 125: ! 126: - setClockType:(int)newValue ! 127: /* setClockType: sets which type of clock is drawn (analog, digital, or ! 128: * sundial). ! 129: */ ! 130: { ! 131: clockType = newValue; ! 132: [face recache]; ! 133: [self display]; ! 134: return self; ! 135: } ! 136: ! 137: - (BOOL)showSeconds ! 138: { ! 139: return showSeconds; ! 140: } ! 141: - (BOOL)showDate ! 142: { ! 143: return showDate; ! 144: } ! 145: - (int)clockType ! 146: { ! 147: return clockType; ! 148: } ! 149: ! 150: ! 151: /* TARGET/ACTION METHODS */ ! 152: ! 153: - changeShowDate:sender; ! 154: { ! 155: return [self setShowDate:[[sender selectedCell] state]]; ! 156: } ! 157: - changeShowSeconds:sender ! 158: { ! 159: return [self setShowSeconds:[[sender selectedCell] state]]; ! 160: } ! 161: - changeClockType:sender ! 162: { ! 163: return [self setClockType:[sender selectedTag]]; ! 164: } ! 165: ! 166: /* String keys used for look-up into string tables */ ! 167: ! 168: static const char *monthKeys[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; ! 169: static const char *months[12]; ! 170: static const char *weekKeys[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; ! 171: static const char *weekdays[7]; ! 172: static const char *romanDays[31] = {"I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII", ! 173: "XIII","XIV","XV","XVI","XVII","XVIII","XIX","XX","XXI","XXII","XXIII", ! 174: "XXIV","XXV","XXVI","XXVII","XXVIII","XXIX","XXX", "XXXI"}; ! 175: ! 176: - setSTable:anObject; ! 177: /* Get string values for months, weekdays from NXStringTable, they are stored ! 178: * as static class variables. ! 179: */ ! 180: { ! 181: int i; ! 182: ! 183: sTable = anObject; ! 184: for (i = 0; i < 7; i++) ! 185: weekdays[i] = [sTable valueForStringKey:weekKeys[i]]; ! 186: for (i = 0; i < 12; i++) ! 187: months[i] = [sTable valueForStringKey:monthKeys[i]]; ! 188: return self; ! 189: } ! 190: ! 191: /* PRIVATE METHODS */ ! 192: ! 193: #define HOURRATIO 0.5 /* Hour hand length compared face size */ ! 194: #define MINUTERATIO 0.85 /* Minute & seconds hands */ ! 195: ! 196: - drawAnalog:(struct tm *)time; ! 197: /* drawAnalog draws the clock hands and date for the analog clock face ! 198: */ ! 199: { ! 200: int min,hour,sec; ! 201: char dateString[15]; ! 202: ! 203: min = time->tm_min; ! 204: hour = time->tm_hour; ! 205: sec = time->tm_sec; ! 206: ! 207: if (showSeconds) ! 208: PSWdrawClockHand (center.x,center.y,-6.0 * sec, radius*MINUTERATIO, ! 209: NX_DKGRAY, 0.0); ! 210: PSWdrawClockHand (center.x,center.y, -(hour+min/60.0) * 30.0, ! 211: radius*HOURRATIO, NX_BLACK, 1.0); ! 212: PSWdrawClockHand (center.x,center.y,- fmod(min,60.0) * 6.0, ! 213: radius*MINUTERATIO, NX_BLACK, 1.0); ! 214: if (showDate) { ! 215: [littleFont set]; ! 216: sprintf(dateString,"%s %s %d",weekdays[time->tm_wday], ! 217: months[time->tm_mon], ! 218: time->tm_mday); ! 219: PSWcenterShow(center.x+1.0,3.0,dateString,NX_WHITE); ! 220: PSWcenterShow(center.x,3.0,dateString,NX_DKGRAY); ! 221: } ! 222: return self; ! 223: } ! 224: ! 225: - drawDigital:(struct tm *)time; ! 226: /* drawDigital draws the time and date for the digital clock face ! 227: */ ! 228: { ! 229: int hour; ! 230: char timeString[10]; ! 231: char dateString[15]; ! 232: ! 233: hour = fmod(time->tm_hour,12); /* get us off military time */ ! 234: if (!hour) hour = 12; /* if noon or midnight */ ! 235: if (showSeconds) ! 236: sprintf(timeString,"%d:%.2d:%.2d",hour, ! 237: time->tm_min, ! 238: time->tm_sec); ! 239: else ! 240: sprintf(timeString,"%d:%.2d", hour,time->tm_min); ! 241: [bigFont set]; ! 242: PSWcenterShow(center.x,center.y-8.0,timeString,NX_BLACK); ! 243: if (showDate) { ! 244: sprintf(dateString,"%s %s %d",weekdays[time->tm_wday], ! 245: months[time->tm_mon], ! 246: time->tm_mday); ! 247: [mediumFont set]; ! 248: PSWcenterShow(center.x,center.y-24.0,dateString,NX_BLACK); ! 249: } ! 250: return self; ! 251: } ! 252: ! 253: #define SHADOWRATIO .95 /* shadow length when compared to radius */ ! 254: #define MARKERRATIO .15 /* height of marker when compared to radius */ ! 255: ! 256: - drawSundial:(struct tm *)time; ! 257: /* drawSundial draws the shadow and date for the sundial clock face ! 258: */ ! 259: { ! 260: float percentOfDay; ! 261: char dateString[15]; ! 262: NXPoint edge; ! 263: ! 264: if (showSeconds) ! 265: PSWdrawSweep (center.x,center.y,-6.0 *time->tm_sec,radius*.75); ! 266: percentOfDay = (time->tm_hour*60 + time->tm_min)/(24.0*60.0); ! 267: edge.x = sin(percentOfDay*PI*2)*radius*SHADOWRATIO; ! 268: edge.y = cos(percentOfDay*PI*2)*radius*SHADOWRATIO; ! 269: PSWdrawShadow(center.x,center.y,edge.x,edge.y,radius*MARKERRATIO); ! 270: if (showDate) { ! 271: [mediumFont set]; ! 272: sprintf(dateString,"%s %s", months[time->tm_mon], ! 273: romanDays[time->tm_mday-1]); ! 274: PSWcenterShow(center.x,12.0,dateString,NX_BLACK); ! 275: } ! 276: return self; ! 277: } ! 278: ! 279: - drawFace:image ! 280: /* drawFace draws the clock face image. This ! 281: * image is composited on screen and then the hands, shadow, ! 282: * whatever is drawn on top of the face for the current time. ! 283: */ ! 284: { ! 285: PSsetgray (NX_LTGRAY); ! 286: NXRectFill (&bounds); // Erase background ! 287: switch (clockType) { ! 288: case ANALOG: PSWdrawAnalogFace(center.x,center.y,radius); ! 289: break; ! 290: case DIGITAL: /* digital "face" is just blank */ ! 291: break; ! 292: case SUNDIAL: PSWdrawSundialFace(center.x,center.y, radius); ! 293: break; ! 294: } ! 295: return self; ! 296: } ! 297: ! 298: ! 299: - drawSelf:(NXRect *)rects :(int)rectCount ! 300: /* Draws face and hands of clock. ! 301: * The clock face image in face is copied into the bounds of the ! 302: * view, and a routine is called to display the current date and time ! 303: */ ! 304: { ! 305: struct tm *localTime; ! 306: struct timeval currentTime; ! 307: ! 308: // If recache was called, or if printing, this will first redraw ! 309: // the clock face by calling drawFace: ! 310: [face composite:NX_COPY toPoint:&bounds.origin]; ! 311: ! 312: gettimeofday (¤tTime, NULL); ! 313: localTime = localtime (&(currentTime.tv_sec)); ! 314: switch (clockType) { ! 315: case ANALOG: [self drawAnalog:localTime]; ! 316: break; ! 317: case DIGITAL: [self drawDigital:localTime]; ! 318: break; ! 319: case SUNDIAL: [self drawSundial:localTime]; ! 320: break; ! 321: } ! 322: return self; ! 323: } ! 324: ! 325: ! 326: - startTimedEntry:(BOOL)fireASAP ! 327: /* startTimedEntry will install the timed entry. If fireASAP is YES, the ! 328: * timed entry is set to fire off as soon as possible (this would be the case ! 329: * at the start of the program, for instance). If fireASAP is NO, then the ! 330: * timed entry is set to fire off in one second (if seconds are being shown) ! 331: * or at the top of the next minute (in anytime between 0 and 60 seconds). ! 332: */ ! 333: { ! 334: double fireIn; ! 335: ! 336: if (fireASAP) fireIn = 0.0; // Fire as soon as possible! ! 337: else if (showSeconds) fireIn = 1.0; // Fire in a second (good enough) ! 338: else { ! 339: struct timeval currentTime; ! 340: gettimeofday (¤tTime, NULL); ! 341: fireIn = 60.0 - (currentTime.tv_sec % 60); // Top of the minute ! 342: } ! 343: ! 344: teNum = DPSAddTimedEntry(fireIn, &ShowTime, self, NX_MODALRESPTHRESHOLD); ! 345: return self; ! 346: } ! 347: ! 348: - stopTimedEntry ! 349: /* Removes the clock timed entry. ! 350: */ ! 351: { ! 352: if (teNum) ! 353: DPSRemoveTimedEntry (teNum); ! 354: teNum = (DPSTimedEntry)0; ! 355: return self; ! 356: } ! 357: ! 358: ! 359: - sizeTo:(NXCoord)w :(NXCoord)h ! 360: /* Overriding sizeTo:: allows us to resize and fix up the clock whenever ! 361: * the size is changed. Figure the radius of the clock (based on the size ! 362: * of the view) and then redraw ! 363: */ ! 364: { ! 365: [super sizeTo:w :h]; ! 366: center.x = bounds.size.width/2.0; ! 367: center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0; ! 368: radius = MIN(center.x, center.y-[mediumFont pointSize]); ! 369: [face setSize:&bounds.size]; ! 370: return self; ! 371: } ! 372: ! 373: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.