home *** CD-ROM | disk | FTP | other *** search
- /*
- * ClockView.m, a simple clock view
- * Author: Ali T. Ozer, NeXT Developer Support Group
- * Created: May 26, 1989 (for version 0.9)
- * Modified: June 14 and Aug 14, 1989 (for version 1.0)
- * Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support
- * Modified some for 3.0 by Ali Ozer, AppKit Group, May 28, 1992
- *
- * Subclass of view to implement a simple clock. This view is pretty generic
- * and can probably be added to any program. The setClockType: method lets
- * you display an analog, digital, or sundial face. You have the option of
- * turning the seconds hand on or off, as well as controlling whether the date
- * is also displayed.
- *
- * You may freely copy, distribute and reuse the code in this example.
- * NeXT disclaims any warranty of any kind, expressed or implied, as to its
- * fitness for any particular use.
- */
-
- #import <appkit/appkit.h>
- #import "ClockView.h"
- #import "Clock.h" // PSwrap routines
- #import <objc/NXStringTable.h>
- #import <string.h>
- #import <sys/time.h>
-
-
- @implementation ClockView:View
-
-
- #define PI (double)3.1415926535897
-
- #define ANALOG 0
- #define DIGITAL 1
- #define SUNDIAL 2
-
-
-
- /* ShowTime() is the timed entry function called by the
- * timed entry mechanism. It first writes the time out on the face
- * of the clock, and then reinstalls the timed entry if the clock is
- * not showing the seconds. If the seconds are being shown, no need to
- * reinstall the timed entry; a second skipped here and then won't matter.
- * If minutes are being shown, we want to make sure that the minute jumps
- * at the next top of the minute, regardless of how long it took to service
- * the timed entry.
- */
- void ShowTime (teNum, now, clock)
- DPSTimedEntry teNum;
- double now;
- id clock;
- {
- [clock display];
- if ([clock showSeconds] == NO) [[clock stopTimedEntry] startTimedEntry:NO];
- }
-
-
-
-
- - initFrame:(const NXRect *)frameRect
- /* initFrame for newly created view, initializes the various parameters,
- * grabs some fonts to be used later.
- */
- {
- [super initFrame:frameRect];
-
- face = [[NXImage allocFromZone:[self zone]] initSize:&bounds.size];
- [face useDrawMethod:@selector(drawFace:) inObject:self];
-
- littleFont = [Font newFont:"Helvetica" size:12 style:0
- matrix:NX_IDENTITYMATRIX];
- mediumFont = [Font newFont:"Times-Roman" size:14 style:0
- matrix:NX_IDENTITYMATRIX];
- bigFont = [Font newFont:"Times-Roman" size:24 style:0
- matrix:NX_IDENTITYMATRIX];
-
- /* Set the default state (analog face, no seconds, date on) */
- clockType = ANALOG;
- showSeconds = NO;
- showDate = YES;
- center.x = bounds.size.width/2.0;
- center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0;
- radius = MIN(center.x,center.y-[mediumFont pointSize]);
- [face recache];
-
- /* Start the time entry. YES indicates that this is the first time */
- [self startTimedEntry:YES];
- [self display];
- return self;
- }
-
-
- - free
- /* Good idea to get rid of the timed entry while freeing...
- */
- {
- [face free];
- [self stopTimedEntry];
- return [super free];
- }
-
-
- /* SET/GET CLOCK PARAMETERS */
-
- - setShowSeconds:(BOOL)newValue
- /* setShowSeconds: sets whether or not the seconds hand is shown.
- * The timed entry must be reinstalled whenever this setting is changed
- * as time to the next firing changes.
- */
- {
- showSeconds = newValue;
- [[self stopTimedEntry] startTimedEntry:NO];
- [self display];
- return self;
- }
-
- - setShowDate:(BOOL)newValue
- /* setShowDate: sets whether or not the date is shown.
- */
- {
- showDate = newValue;
- [self display];
- return self;
- }
-
- - setClockType:(int)newValue
- /* setClockType: sets which type of clock is drawn (analog, digital, or
- * sundial).
- */
- {
- clockType = newValue;
- [face recache];
- [self display];
- return self;
- }
-
- - (BOOL)showSeconds
- {
- return showSeconds;
- }
- - (BOOL)showDate
- {
- return showDate;
- }
- - (int)clockType
- {
- return clockType;
- }
-
-
- /* TARGET/ACTION METHODS */
-
- - changeShowDate:sender;
- {
- return [self setShowDate:[[sender selectedCell] state]];
- }
- - changeShowSeconds:sender
- {
- return [self setShowSeconds:[[sender selectedCell] state]];
- }
- - changeClockType:sender
- {
- return [self setClockType:[sender selectedTag]];
- }
-
- /* String keys used for look-up into string tables */
-
- static const char *monthKeys[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
- static const char *months[12];
- static const char *weekKeys[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
- static const char *weekdays[7];
- static const char *romanDays[31] = {"I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII",
- "XIII","XIV","XV","XVI","XVII","XVIII","XIX","XX","XXI","XXII","XXIII",
- "XXIV","XXV","XXVI","XXVII","XXVIII","XXIX","XXX", "XXXI"};
-
- - setSTable:anObject;
- /* Get string values for months, weekdays from NXStringTable, they are stored
- * as static class variables.
- */
- {
- int i;
-
- sTable = anObject;
- for (i = 0; i < 7; i++)
- weekdays[i] = [sTable valueForStringKey:weekKeys[i]];
- for (i = 0; i < 12; i++)
- months[i] = [sTable valueForStringKey:monthKeys[i]];
- return self;
- }
-
- /* PRIVATE METHODS */
-
- #define HOURRATIO 0.5 /* Hour hand length compared face size */
- #define MINUTERATIO 0.85 /* Minute & seconds hands */
-
- - drawAnalog:(struct tm *)time;
- /* drawAnalog draws the clock hands and date for the analog clock face
- */
- {
- int min,hour,sec;
- char dateString[15];
-
- min = time->tm_min;
- hour = time->tm_hour;
- sec = time->tm_sec;
-
- if (showSeconds)
- PSWdrawClockHand (center.x,center.y,-6.0 * sec, radius*MINUTERATIO,
- NX_DKGRAY, 0.0);
- PSWdrawClockHand (center.x,center.y, -(hour+min/60.0) * 30.0,
- radius*HOURRATIO, NX_BLACK, 1.0);
- PSWdrawClockHand (center.x,center.y,- fmod(min,60.0) * 6.0,
- radius*MINUTERATIO, NX_BLACK, 1.0);
- if (showDate) {
- [littleFont set];
- sprintf(dateString,"%s %s %d",weekdays[time->tm_wday],
- months[time->tm_mon],
- time->tm_mday);
- PSWcenterShow(center.x+1.0,3.0,dateString,NX_WHITE);
- PSWcenterShow(center.x,3.0,dateString,NX_DKGRAY);
- }
- return self;
- }
-
- - drawDigital:(struct tm *)time;
- /* drawDigital draws the time and date for the digital clock face
- */
- {
- int hour;
- char timeString[10];
- char dateString[15];
-
- hour = fmod(time->tm_hour,12); /* get us off military time */
- if (!hour) hour = 12; /* if noon or midnight */
- if (showSeconds)
- sprintf(timeString,"%d:%.2d:%.2d",hour,
- time->tm_min,
- time->tm_sec);
- else
- sprintf(timeString,"%d:%.2d", hour,time->tm_min);
- [bigFont set];
- PSWcenterShow(center.x,center.y-8.0,timeString,NX_BLACK);
- if (showDate) {
- sprintf(dateString,"%s %s %d",weekdays[time->tm_wday],
- months[time->tm_mon],
- time->tm_mday);
- [mediumFont set];
- PSWcenterShow(center.x,center.y-24.0,dateString,NX_BLACK);
- }
- return self;
- }
-
- #define SHADOWRATIO .95 /* shadow length when compared to radius */
- #define MARKERRATIO .15 /* height of marker when compared to radius */
-
- - drawSundial:(struct tm *)time;
- /* drawSundial draws the shadow and date for the sundial clock face
- */
- {
- float percentOfDay;
- char dateString[15];
- NXPoint edge;
-
- if (showSeconds)
- PSWdrawSweep (center.x,center.y,-6.0 *time->tm_sec,radius*.75);
- percentOfDay = (time->tm_hour*60 + time->tm_min)/(24.0*60.0);
- edge.x = sin(percentOfDay*PI*2)*radius*SHADOWRATIO;
- edge.y = cos(percentOfDay*PI*2)*radius*SHADOWRATIO;
- PSWdrawShadow(center.x,center.y,edge.x,edge.y,radius*MARKERRATIO);
- if (showDate) {
- [mediumFont set];
- sprintf(dateString,"%s %s", months[time->tm_mon],
- romanDays[time->tm_mday-1]);
- PSWcenterShow(center.x,12.0,dateString,NX_BLACK);
- }
- return self;
- }
-
- - drawFace:image
- /* drawFace draws the clock face image. This
- * image is composited on screen and then the hands, shadow,
- * whatever is drawn on top of the face for the current time.
- */
- {
- PSsetgray (NX_LTGRAY);
- NXRectFill (&bounds); // Erase background
- switch (clockType) {
- case ANALOG: PSWdrawAnalogFace(center.x,center.y,radius);
- break;
- case DIGITAL: /* digital "face" is just blank */
- break;
- case SUNDIAL: PSWdrawSundialFace(center.x,center.y, radius);
- break;
- }
- return self;
- }
-
-
- - drawSelf:(NXRect *)rects :(int)rectCount
- /* Draws face and hands of clock.
- * The clock face image in face is copied into the bounds of the
- * view, and a routine is called to display the current date and time
- */
- {
- struct tm *localTime;
- struct timeval currentTime;
-
- // If recache was called, or if printing, this will first redraw
- // the clock face by calling drawFace:
- [face composite:NX_COPY toPoint:&bounds.origin];
-
- gettimeofday (¤tTime, NULL);
- localTime = localtime (&(currentTime.tv_sec));
- switch (clockType) {
- case ANALOG: [self drawAnalog:localTime];
- break;
- case DIGITAL: [self drawDigital:localTime];
- break;
- case SUNDIAL: [self drawSundial:localTime];
- break;
- }
- return self;
- }
-
-
- - startTimedEntry:(BOOL)fireASAP
- /* startTimedEntry will install the timed entry. If fireASAP is YES, the
- * timed entry is set to fire off as soon as possible (this would be the case
- * at the start of the program, for instance). If fireASAP is NO, then the
- * timed entry is set to fire off in one second (if seconds are being shown)
- * or at the top of the next minute (in anytime between 0 and 60 seconds).
- */
- {
- double fireIn;
-
- if (fireASAP) fireIn = 0.0; // Fire as soon as possible!
- else if (showSeconds) fireIn = 1.0; // Fire in a second (good enough)
- else {
- struct timeval currentTime;
- gettimeofday (¤tTime, NULL);
- fireIn = 60.0 - (currentTime.tv_sec % 60); // Top of the minute
- }
-
- teNum = DPSAddTimedEntry(fireIn, &ShowTime, self, NX_MODALRESPTHRESHOLD);
- return self;
- }
-
- - stopTimedEntry
- /* Removes the clock timed entry.
- */
- {
- if (teNum)
- DPSRemoveTimedEntry (teNum);
- teNum = (DPSTimedEntry)0;
- return self;
- }
-
-
- - sizeTo:(NXCoord)w :(NXCoord)h
- /* Overriding sizeTo:: allows us to resize and fix up the clock whenever
- * the size is changed. Figure the radius of the clock (based on the size
- * of the view) and then redraw
- */
- {
- [super sizeTo:w :h];
- center.x = bounds.size.width/2.0;
- center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0;
- radius = MIN(center.x, center.y-[mediumFont pointSize]);
- [face setSize:&bounds.size];
- return self;
- }
-
- @end