This is Clock.m in view mode; [Download] [Up]
// Clock.m, 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)
//
// Modifier: Jiro Nakamura, Independent NeXT Developer
// Modified: May 11, 1990 (for Cassandra)
//
// Subclass of view to implement a simple analog or digital clock. This view is
// pretty generic and can probably be added to any program. Different clock
// faces can be set through the setBackgroundGray: and setClockGray: methods;
// you can also specify TIFF files as background images for the clock through
// the setClockFaceToBitmapNamed: method. Finally you have the option of
// turning the seconds hand on or off.
static char authorid[] = "$Author: jiro $";
static char rcsid[] = "$Id: Clock.m,v 1.3 90/12/03 01:55:16 jiro Exp Locker: jiro $";
#import "Clock.h"
#import "ClockPS.h" // PSwrap routines
#import <dpsclient/wraps.h>
#import <appkit/nextstd.h>
#import <appkit/Application.h>
#import <appkit/Button.h>
#import <appkit/Window.h>
#import <objc/typedstream.h>
#import <string.h>
#import <sys/time.h>
#import "calendar.h"
// Defining DEBUG for Clock can sometimes get very screen messy
// since Cassandra uses it so often
#ifdef DEBUG
#undef DEBUG
#define DEBUG2
#endif
#define MAX_CVIEW_WIDTH 14
@implementation Clock
// 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];
}
// newFrame creates the view and initializes the various paramaters. The
// constants below determine the lengths of the clock hands.
#define HOURRATIO 0.2 /* Hour hand length compared face size */
#define MINUTERATIO 0.35 /* Minute & seconds hands */
+ newFrame:(const NXRect *)frm
{
self = [super newFrame:frm];
[self initMoreStuff];
return self;
}
- initMoreStuff
{
char dumbChar[5];
// dumb references to some static constants, just
// to avoid warning messages from the compiler (Garance)
dumbChar[0] = authorid[0]; // dumb
dumbChar[1] = rcsid[0]; // de dumb
// Set the default state (no special face, no seconds, grays).
clockFace = nil;
showSeconds = NO;
showDate = NO;
backgroundGray = NX_LTGRAY;
clockGray = NX_BLACK;
[self computeNewSizes];
// Start the time entry. YES indicates that this is the first time.
[self startTimedEntry:YES];
return self;
}
// Good idea to get rid of the timed entry while freeing...
- free
{
[self stopTimedEntry];
return [super free];
}
// 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).
- startTimedEntry:(BOOL)fireASAP
{
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
}
clockTE = DPSAddTimedEntry(fireIn, &ShowTime, self, NX_MODALRESPTHRESHOLD);
return self;
}
// stopTimedEntry stops the timed entry. Don't call this method unless
// the timed entry is installed an running.
- stopTimedEntry
{
DPSRemoveTimedEntry (clockTE);
return self;
}
// drawSelf:: displays the face of the clock, showing the correct time.
// The seconds hand will be shown only if desired.
- drawSelf:(NXRect *)rects :(int)rectCount
{
int min, hour, sec;
struct tm *localTime;
struct timeval currentTime;
static char buf[10];
extern const char *shortWeekDays[7];
extern const char *shortMonths[12];
// Additions by Jiro for target/action response
//Send the <action> to the <target>.
// Mod Jan 6, 1990
[target perform:action with:self];
gettimeofday (¤tTime, NULL);
localTime = localtime (&(currentTime.tv_sec));
min = localTime->tm_min;
hour = localTime->tm_hour;
sec = localTime->tm_sec;
// Erase the background and composite the clock face
// in if there is one.
// Note that we erase even though we might have a bitmap;
// this way if the
// bitmap is smaller than the view it'll still look OK. In a more
// efficient frame of mind, one could try to avoid unnecessary fills.
if (backgroundGray >= 0.0 && backgroundGray <= 1.0) {
PSsetgray (backgroundGray);
NXRectFill (&bounds);
}
// If we have a clockface, let's display it
if(clockFace != nil)
{
#ifdef DEBUG
fprintf(stderr,"%s: Compositing clock face.\n",
PROGNAME);
#endif
[clockFace composite:NX_COPY toPoint:&compositeTo];
}
switch( clockType)
{
case CLOCK_ANALOG:
// Display the seconds arm, if desired, and then
// the hours and minutes.
if (showSeconds)
drawClockHand (-6.0 * sec, minuteLen,
clockGray, 0.0);
drawClockHand (- (hour + min / 60.0) * 30.0, hourLen,
clockGray, 2.0);
drawClockHand (- fmod(min, 60.0) * 6.0, minuteLen,
clockGray, 2.0);
if( showDate)
{
sprintf(buf, "%s %2d",
shortMonths[localTime->tm_mon],
localTime->tm_mday);
drawClockString( 0, 7, 8, buf);
drawClockString( 0, -10, 8, (char *)
shortWeekDays[localTime->tm_wday]);
}
break;
case CLOCK_DIGITAL:
if( !militaryTime)
{
if( hour >= 12 )
hour -= 12;
if( hour == 0 )
hour = 12;
}
sprintf(buf, "%2d:%02d", hour, min);
drawClockString( 0, 6, 18, buf);
sprintf(buf,"%s %s %2d", (char *)
shortWeekDays[localTime->tm_wday],
shortMonths[localTime->tm_mon], localTime->tm_mday);
drawClockString( 0, -1, 8, buf);
PSmoveto( -10, -5);
PSlineto( 10, -5);
PSstroke();
drawClockString( 0, -14, 8, eventTime);
drawClockString( 0, -21, 8, eventMessage);
break;
default:
fprintf(stderr,"%s: Unrecognized clock type: %d\n",
PROGNAME, clockType);
}
return self;
}
// Set the clock face to the specified bitmap. If bitmapName is NULL,
// then we have no clock face.
- setClockFaceToBitmapNamed:(const char *)bitmapName
{
#ifdef DEBUG2
fprintf(stderr, "%s: Setting clockface bitmap to %s\n",
PROGNAME, bitmapName);
#endif
if (bitmapName && (clockFace = [NXImage findImageNamed:bitmapName]))
{
[self computeBitmapLocation];
}
else
{
#ifdef DEBUG2
fprintf(stderr,"%s: Couldn't find a icon for %s.\n",
PROGNAME, bitmapName);
#endif
clockFace = nil;
}
[self display];
return self;
}
// Compute and cache the location where the clock face should be
// composited into. This method should be invoked everytime the clock face
// changes or the view is resized.
//
// We assume that the view is translated so that the origin is at the center.
- computeBitmapLocation
{
if (clockFace) {
NXSize bitmapSize;
[clockFace getSize:&bitmapSize];
compositeTo.x = - bitmapSize.width / 2.0;
compositeTo.y = - bitmapSize.height / 2.0;
}
return self;
}
// Figure the lengths of the hands (based on the size of the view) and
// translate the view so that the center is (0,0).
- computeNewSizes
{
// Translate view so that the center is 0,0.
[self setDrawOrigin:-bounds.size.width/2.0 :-bounds.size.height/2.0];
// Compute the hand lengths and cache bitmap location.
[self computeBitmapLocation];
minuteLen = MIN(bounds.size.width, bounds.size.height) * MINUTERATIO;
hourLen = MIN(bounds.size.width, bounds.size.height) * HOURRATIO;
return self;
}
// Overriding sizeTo:: allows us to resize and fix up the clock whenever
// the size is changed.
- sizeTo:(NXCoord)w :(NXCoord)h
{
[super sizeTo:w :h];
[self computeNewSizes];
return self;
}
// setShowSecondsToBool: 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.
- setShowSecondsToBool:(BOOL)seconds
{
showSeconds = seconds;
[[self stopTimedEntry] startTimedEntry:NO];
[self display];
return self;
}
- setShowDateToBool: (BOOL) date
{
showDate = date;
[[self stopTimedEntry] startTimedEntry:NO];
[self display];
return self;
}
- setMilitaryTimeToBool: (BOOL) mt
{
militaryTime = mt;
[[self stopTimedEntry] startTimedEntry:NO];
[self display];
return self;
}
- setClockTypeToInt: (int) type
{
clockType = type;
[[self stopTimedEntry] startTimedEntry:NO];
[self display];
return self;
}
// Methods to set/get background and clock hand colors.
- setBackgroundGrayToFloat:(float)gray
{
backgroundGray = gray;
[self display];
return self;
}
- setClockGrayToFloat:(float)gray
{
clockGray = gray;
[self display];
return self;
}
- setMessage: (char *) buf1 : (char *) buf2
{
strncpy( eventTime, buf1, MAX_CVIEW_WIDTH);
strncpy( eventMessage, buf2, MAX_CVIEW_WIDTH);
if( clockType == CLOCK_DIGITAL)
[self display];
return self;
}
// Methods to return the values of the above parameters
- (BOOL)showSeconds
{
return showSeconds;
}
- (BOOL)showDate
{
return showDate;
}
- (const char *)clockFaceBitmapName
{
return [clockFace name];
}
- (float)backgroundGray
{
return backgroundGray;
}
- (float)clockGray
{
return clockGray;
}
// Finally, the IB-callable (target/action) versions of the above methods.
- setClockFace:sender
{
return [self setClockFaceToBitmapNamed:[sender stringValue]];
}
- setShowSeconds:sender
{
return [self setShowSecondsToBool:[sender state]];
}
- setShowDate:sender
{
return [self setShowDateToBool:[sender state]];
}
- setBackgroundGray:(id)sender
{
return [self setBackgroundGrayToFloat:[sender floatValue]];
}
- setClockGray:(id)sender
{
return [self setClockGrayToFloat:[sender floatValue]];
}
// Additions to allow target/action response
// Mods by Jiro Nakamura, Jan 6, 1990
- setTarget: (id) aTarget
{
target = aTarget;
return self;
}
- setAction: (SEL) anAction
{
action = anAction;
return self;
}
// Archiving methods.
- awake
{
[self computeNewSizes];
[self startTimedEntry:YES];
return self;
}
- write:(NXTypedStream *)stream
{
const char *bitmapName = [self clockFaceBitmapName];
[super write:stream];
NXWriteTypes (stream, "ffi*",
&backgroundGray, &clockGray, &showSeconds, &bitmapName);
return self;
}
- read:(NXTypedStream *)stream
{
char *bitmapName;
[super read:stream];
NXReadTypes (stream, "ffi*",
&backgroundGray, &clockGray, &showSeconds, &bitmapName);
[self setClockFaceToBitmapNamed:bitmapName];
if (bitmapName) free (bitmapName);
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.