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