//----------------------------------------------------------------------------------------------------
//
//	Clock
//
//	Inherits From:		Control
//
//	Declared In:		Clock.h
//
//	Disclaimer
//
//		You may freely copy, distribute and reuse this software and its
//		associated documentation. I disclaim any warranty of any kind, 
//		expressed or implied, as to its fitness for any particular use.
//
//----------------------------------------------------------------------------------------------------
#import "Clock.h"


static const char*  ClockPboardType = "ClockPboardType";

#define	TIMER_RELATIVE			0x0		//  compute relative time values.
#define	TIMER_REALTIME		0x1		//  stay synced with system time.

#define	DISPLAY_CLOCK			0x0		//  display clock time
#define	DISPLAY_ALARM			0x1		//  display alarm time
#define	DISPLAY_OTHER			0x2		//  display user specified time

#define	ALARM_STOPWATCH		0x0		//  execute after fixed time
#define	ALARM_TIME			0x1		//  match on time
#define	ALARM_TIMEDATE		0x2		//  match on time and date
#define	ALARM_TIMEWEEKDAY	0x4		//  match on time and weekday

#define	AWAKE_STOPPED		0x0		//  no timed entry on awake
#define	AWAKE_RUNNING		0x1		//  add timed entry on awake
#define	AWAKE_PREVENT		0x2		//  should but don't (for pboard)


@implementation Clock

//----------------------------------------------------------------------------------------------------
//	Private Methods
//----------------------------------------------------------------------------------------------------
- (int) _timerMode
{
	return privateFlags.timerMode;
}


- (int) _alarmMode
{
	return privateFlags.alarmMode;
}


- (int) _displayMode
{
	return privateFlags.displayMode;
}


- (BOOL) _backgroundIsColor
{
	return privateFlags.backgroundIsColor;
}


- _backgroundIsColor: (BOOL)aFlag
{
	privateFlags.backgroundIsColor = aFlag;
	[self update];
	return self;
}


- _alarmMode: (int) aMode
{
	privateFlags.alarmMode = aMode;
	return self;
}


- _timerMode: (int) aMode
{
	privateFlags.timerMode = aMode;
	return self;
}


- (int) _previousClock
{
	return previousClock;
}


- (struct tm*) _systemTime
{
    	struct  timeval greenwich;
	struct  tm*	currentTime;
		
    	gettimeofday (&greenwich, NULL);
    	currentTime = (localtime (&(greenwich.tv_sec)));
	return currentTime;
}


- (int) _lastDayOfMonth: (struct tm*) aTime
{
	int	lastDayOfMonth[] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	
	if (MONTH (aTime) == 1) lastDayOfMonth[1] = (YEAR (aTime) % 4 ? 28 : 29);
	return lastDayOfMonth[MONTH (aTime)];
}


- _addDayToClockTime
{
	//  Increment current clockTimeAndDate by one day.
	
	++DAY(&clockTimeAndDate);
	
	if (DAY(&clockTimeAndDate) >  [self _lastDayOfMonth:&clockTimeAndDate])
		{
		DAY(&clockTimeAndDate) = 1;
		if (++MONTH (&clockTimeAndDate) > 11)
			{
			MONTH (&clockTimeAndDate) = 0;
			YEAR (&clockTimeAndDate) =
			 	(++YEAR (&clockTimeAndDate) > 1999 ? 0 : YEAR (&clockTimeAndDate));
			}
		}
		
	WEEKDAY(&clockTimeAndDate) = [self weekDay: &clockTimeAndDate];
	return self;
}
	

- (struct tm*) _computeClockTimeRelativeTo: (struct tm*) aTime
{
	//  User has manually set time, now increment based on system clock delta.
	//  (i.e. if it changed by 2 seconds, increment user time by 2 seconds.)
	
	int	newClock;
	
	previousClock = (privateFlags.initRelativeTimerMode ? SECONDS (aTime) : previousClock);
	newClock = SECONDS (&clockTimeAndDate) + (SECONDS (aTime) - previousClock);
	previousClock = SECONDS (aTime);
	privateFlags.initRelativeTimerMode = NO;
	
	HOUR (&clockTimeAndDate) = newClock / 3600;
	if (HOUR (&clockTimeAndDate) == 24) 
		{
		HOUR (&clockTimeAndDate) = 0;
		[self _addDayToClockTime];
		}
	MINUTE (&clockTimeAndDate) = (newClock % 3600) / 60;
	SECOND (&clockTimeAndDate) = ((newClock % 3600) % 60);

	return &clockTimeAndDate;
}


- _updateClockTime
{
	//  Update the clock time as appropriate (REALTIME stays synced with system
	//  clock, RELATIVE computes based on system clock delta).
	
	if (privateFlags.timerMode == TIMER_REALTIME)
		[self clockTimeAndDate: [self _systemTime]];
	else
		[self  _computeClockTimeRelativeTo: [self _systemTime]];

	return self;
}


- _checkAlarm
{
	//  Check for match of current Clock time and Alarm time. Seconds will be checked
	//  within some tolerance (current time + tolerance) in case timed entry was delayed.  
	//  Check date or day of week if appropriate.
	
	int	alarmSeconds = SECONDS (&alarmTimeAndDate);
	int	clockSeconds = SECONDS (&clockTimeAndDate);
	
	if (clockSeconds < alarmSeconds || clockSeconds - 10 > alarmSeconds)
		{
		privateFlags.alarmSleep = NO;
		return self;
		}

	if (privateFlags.alarmSleep) return self;
	privateFlags.alarmSleep = YES;

	if (privateFlags.alarmMode == ALARM_TIMEDATE)
		{
		if (DAY(&alarmTimeAndDate) != DAY(&clockTimeAndDate)) return self;
		if (MONTH(&alarmTimeAndDate) != MONTH(&clockTimeAndDate)) return self;
		if (YEAR(&alarmTimeAndDate) != YEAR(&clockTimeAndDate)) return self;
		}
	else if (privateFlags.alarmMode == ALARM_TIMEWEEKDAY)
		if (WEEKDAY(&alarmTimeAndDate) != WEEKDAY(&clockTimeAndDate)) return self;

	[self executeAlarmAction: nil];
	return self;
}


void Tick (timedEntry, now, self)
	DPSTimedEntry	timedEntry;
	double			now;
	void*			self;
{
	// Callback function for DPS timed entry.
		
	struct  _clock{ @defs (Clock) } *clock = (struct _clock*) self;
	[(id)self _updateClockTime];
	if (clock->flags.isAlarmEnabled && clock->privateFlags.alarmMode) [(id)self _checkAlarm];
	if (clock->flags.wantsDisplay && clock->privateFlags.displayMode == DISPLAY_CLOCK)
		{
		clock->displayTimeAndDate = &clock->clockTimeAndDate;
		[(id)self  display];
		}
}


- _addTimedEntry
{
    	timedEntry = DPSAddTimedEntry(1.0, Tick, (void*) self, NX_MODALRESPTHRESHOLD);
	flags.activateOnAwake = YES;
    	NXPing();
    	return self;
}


- _removeTimedEntry
{
	DPSRemoveTimedEntry (timedEntry);
	timedEntry = 0;
	flags.activateOnAwake = NO;
	return self;
}


- (NXPoint) _compositePointForImage: anImage
{
	NXSize	iconSize;

	[anImage getSize: &iconSize];
	
	if  ( iconSize.width < NX_WIDTH (&bounds) ) 
		NX_X (&bounds) += (NX_WIDTH (&bounds) - iconSize.width ) / 2.0;
	
	if  ( iconSize.height < NX_HEIGHT (&bounds) ) 
		NX_Y (&bounds) += (NX_HEIGHT (&bounds) - iconSize.width ) / 2.0;

	return bounds.origin;
}


- _nextText: sender
{
	//  This method will allow an action to be sent and nextText to be selected when 
	//  'return' is entered.  Only works with TextFields and TextField subclasses. Used by 	//  'takeIntValueFrom'  methods.

	if ([sender isKindOf: [TextField class]]) 
		if ([[sender nextText] respondsTo: @selector(selectText:)])
			[[sender nextText] selectText:nil];

	return self;
}


//----------------------------------------------------------------------------------------------------
//	Initialization and Freeing
//----------------------------------------------------------------------------------------------------
- initFrame: (const NXRect *)aRect
{
	[super initFrame:aRect];
	
	[self setCell: [[ActionCell allocFromZone:[self zone]] init]];
	[self setDisplayEnabled: YES];
	[self wantsMilitaryTime: NO];
	[self wantsSeconds: YES];
	[self wantsDate: YES];
	[self isDraggable: YES];
	[self willAcceptDrop: YES];
	[self enableAlarm: NO];
	[self wantsAlarmIndicator: NO];
	[self backgroundColor: NX_COLORLTGRAY];
	[self clockTimeAndDate: [self _systemTime]];
	[self alarmTimeAndDate: &clockTimeAndDate];

	flags.activateOnAwake = AWAKE_STOPPED;
	privateFlags.displayMode = DISPLAY_CLOCK;
	privateFlags.timerMode = TIMER_REALTIME;
	privateFlags.alarmMode = ALARM_TIME;
	displayTimeAndDate = &clockTimeAndDate;

	return self;
}


- free
{
	[self stop:nil];
	if (clockFace) [clockFace free];
	if (backgroundImage) [backgroundImage free];
	if (alarmSound) [alarmSound free];
	return [super free];
}


//----------------------------------------------------------------------------------------------------
//	Accessing Display
//----------------------------------------------------------------------------------------------------
- backgroundImage: (NXImage*) anImage
{
	//  Background images will be scaled to fit.
	
	if ( (backgroundImage) && (backgroundImage != anImage) ) [backgroundImage free];
	backgroundImage = [anImage copyFromZone: [self zone]];
	privateFlags.backgroundIsColor = NO;
	[backgroundImage setScalable:YES];
	[self update];
	return self;
}
	
	
- backgroundColor: (NXColor) aColor
{
	privateFlags.backgroundIsColor = YES;
	backgroundColor = aColor;
	[self update];
	return self;
}


- clockFace: (NXImage*) anImage
{
	//  ClockFace images will be centered.
	
	if ( (clockFace) && (clockFace != anImage) ) [clockFace free];
	clockFace = [anImage copyFromZone: [self zone]];
	[self update];
	return self;
}


- (NXImage*) backgroundImage
{
	return backgroundImage;
}


- (NXColor) backgroundColor
{
	return backgroundColor;
}


- (NXImage*) clockFace
{
	return clockFace;
}


- setDisplayEnabled: (BOOL) aFlag;
{
	flags.wantsDisplay = aFlag;
	[self update];
	return self;
}


- wantsMilitaryTime: (BOOL) aFlag
{
	flags.wantsMilitaryTime = aFlag;
	[self update];
	return self;
}


- wantsDate: (BOOL) aFlag
{
	flags.wantsDate = aFlag;
	[self update];
	return self;
}


- wantsSeconds: (BOOL) aFlag
{
	flags.wantsSeconds = aFlag;
	[self update];
	return self;
}


- (BOOL) isDisplayEnabled
{
	return flags.wantsDisplay;
}


- (BOOL) wantsMilitaryTime
{
	return flags.wantsMilitaryTime;
}


- (BOOL) wantsDate
{
	return flags.wantsDate;
}


- (BOOL) wantsSeconds
{
	return flags.wantsSeconds;
}


//----------------------------------------------------------------------------------------------------
//	Accessing Clock Time and Date
//----------------------------------------------------------------------------------------------------
- clockTimeAndDate: (struct tm*) theClockTimeAndDate
{

	memcpy (&clockTimeAndDate,  theClockTimeAndDate, sizeof (struct tm));
	return self;
}


- clockSecond: (int) theSecond
{
	if (theSecond < 0) theSecond = 0; else if (theSecond > 59) theSecond = 59;
	SECOND(&clockTimeAndDate) = theSecond;
	return self;
}


- clockMinute: (int) theMinute
{
	if (theMinute < 0) theMinute = 0; else if (theMinute > 59) theMinute = 59;
	MINUTE(&clockTimeAndDate) = theMinute;
	return self;
}


- clockHour: (int) theHour
{
	if (theHour < 0) theHour = 0; else if (theHour > 23) theHour = 23;
	HOUR(&clockTimeAndDate) = theHour;
	return self;
}

- clockDay: (int) theDay
{
	if (theDay < 1) 
		theDay = 1; 
	else if (theDay > [self _lastDayOfMonth:&clockTimeAndDate]) 
		theDay =  [self _lastDayOfMonth:&clockTimeAndDate];

	DAY(&clockTimeAndDate) = theDay;
	WEEKDAY(&clockTimeAndDate) = [self weekDay: &clockTimeAndDate];
	return self;
}


- clockMonth: (int) theMonth
{
	if (theMonth < 0) theMonth = 0; else if (theMonth > 11) theMonth = 11;
	MONTH(&clockTimeAndDate) = theMonth;
	WEEKDAY(&clockTimeAndDate) = [self weekDay: &clockTimeAndDate];
	[self clockDay: [self clockDay]];
	return self;
}


- clockYear: (int) theYear
{
	if (theYear < 0) theYear = 0; else if (theYear > 99) theYear = 99;
	YEAR(&clockTimeAndDate) = theYear;
	WEEKDAY(&clockTimeAndDate) = [self weekDay: &clockTimeAndDate];
	[self clockDay: [self clockDay]];
	return self;
}


- clockWeekday: (int) theWeekday
{
	if (theWeekday < 0) theWeekday = 0; else if (theWeekday > 6) theWeekday = 6;
	WEEKDAY(&clockTimeAndDate) = theWeekday;
	return self;
}


- (struct tm*) clockTimeAndDate
{
	return &clockTimeAndDate;
}


- (int) clockSecond
{
	return SECOND(&clockTimeAndDate);
}


- (int) clockMinute
{
	return MINUTE(&clockTimeAndDate);
}


- (int) clockHour
{
	return HOUR(&clockTimeAndDate);
}


- (int) clockDay
{
	return DAY(&clockTimeAndDate);
}


- (int) clockMonth
{
	return MONTH(&clockTimeAndDate);
}


- (int) clockYear
{
	return YEAR(&clockTimeAndDate);
}


- (int) clockWeekday
{
	return WEEKDAY(&clockTimeAndDate);
}


//----------------------------------------------------------------------------------------------------
//	Setting. Enabling And Executing the Alarm
//----------------------------------------------------------------------------------------------------
- enableAlarm: (BOOL) aFlag
{
	//  Alarm must be activated (and clock running) before it will execute.  
	//  Merely setting the alarm will not activate it.
	
	flags.isAlarmEnabled = aFlag;
	privateFlags.alarmSleep = NO;
	return self;
}


- (BOOL) isAlarmEnabled
{
	return flags.isAlarmEnabled;
}


- wantsAlarmIndicator: (BOOL) aFlag
{
	//  Displays a visible indicater if the alarm is enabled and this flag is YES.
	
	flags.wantsAlarmIndicator = aFlag;
	[self update];
	return self;
}


- (BOOL) wantsAlarmIndicator
{
	return flags.wantsAlarmIndicator;
}


- setAlarmTime: (struct tm*) theTime
{
	//  Sets alarm time to hour, minute, and second values of theTime.  All
	//  other values will be ignored.  Alarm will execute if a match of the specified
	//   time occurs. The setTimeAndDate method is helpful in filling a tm struct.
		
	[self alarmTimeAndDate: theTime];
	[self enableAlarm: YES];
	privateFlags.alarmMode = ALARM_TIME;
	return self;
}

	
- setAlarmTimeAndDate: (struct tm*) theTimeAndDate;
{
	//  Sets alarm time and date to the hour, minute, second, day, month, and
	//  year values of theTimeAndDate.  All other values will be ignored.  
	//  Alarm will execute if a match on the specified time and date 
	//  occurs. The setTimeAndDate method is helpful in filling a tm struct.

	[self alarmTimeAndDate: theTimeAndDate];
	[self enableAlarm: YES];
	privateFlags.alarmMode = ALARM_TIMEDATE;
	return self;
}

	
- setAlarmTimeAndWeekday: (struct tm*) theTimeAndWeekday;
{
	//  Sets alarm time and weekday to the hour, minute, second, and weekday
	//  values of theTimeAndWeekday.  All other values will be ignored.  
	//  Alarm will execute if a match on the specified time and weekday 
	//  occurs. For example, every Monday at 12:00. The setTimeAndDate
	//  method is helpful in filling a tm struct.
	
	[self alarmTimeAndDate: theTimeAndWeekday];
	[self enableAlarm: YES];
	privateFlags.alarmMode = ALARM_TIMEWEEKDAY;
	return self;
}


- executeAlarmAction: sender
{
	//  Called if match on alarm.
	
	if (alarmSound) [alarmSound play];
	
	if ([self target])
		if ([[self target] respondsTo: [self action]])
			[[self target] perform:[self action] with:self];
			
	return self;
}
			
			
//----------------------------------------------------------------------------------------------------
//	Accessing Alarm Time and Date
//----------------------------------------------------------------------------------------------------
- alarmTimeAndDate: (struct tm*) theAlarmTimeAndDate
{
	memcpy (&alarmTimeAndDate,  theAlarmTimeAndDate, sizeof (struct tm));
	return self;
}


- alarmSecond: (int) theSecond
{
	if (theSecond < 0) theSecond = 0; else if (theSecond > 59) theSecond = 59;
	SECOND(&alarmTimeAndDate) = theSecond;
	return self;
}


- alarmMinute: (int) theMinute
{
	if (theMinute < 0) theMinute = 0; else if (theMinute > 59) theMinute = 59;
	MINUTE(&alarmTimeAndDate) = theMinute;
	return self;
}


- alarmHour: (int) theHour
{
	if (theHour < 0) theHour = 0; else if (theHour > 23) theHour = 23;
	HOUR(&alarmTimeAndDate) = theHour;
	return self;
}

- alarmDay: (int) theDay
{
	if (theDay < 1) 
		theDay = 1; 
	else if (theDay > [self _lastDayOfMonth:&alarmTimeAndDate]) 
		theDay = [self _lastDayOfMonth:&alarmTimeAndDate];

	DAY(&alarmTimeAndDate) = theDay;
	WEEKDAY(&alarmTimeAndDate) = [self weekDay: &alarmTimeAndDate];
	return self;
}


- alarmMonth: (int) theMonth
{
	if (theMonth < 0) theMonth = 0; else if (theMonth > 11) theMonth = 11;
	MONTH(&alarmTimeAndDate) = theMonth;
	WEEKDAY(&alarmTimeAndDate) = [self weekDay: &alarmTimeAndDate];
	[self alarmDay: [self alarmDay]];
	return self;
}


- alarmYear: (int) theYear
{
	if (theYear < 0) theYear = 0; else if (theYear > 99) theYear = 99;
	YEAR(&alarmTimeAndDate) = theYear;
	WEEKDAY(&alarmTimeAndDate) = [self weekDay: &alarmTimeAndDate];
	[self alarmDay: [self alarmDay]];
	return self;
}


- alarmWeekday: (int) theWeekday
{
	if (theWeekday < 0) theWeekday = 0; else if (theWeekday > 6) theWeekday = 6;
	WEEKDAY(&alarmTimeAndDate) = theWeekday;
	return self;
}


- (struct tm*) alarmTimeAndDate
{
	return &alarmTimeAndDate;
}


- (int) alarmSecond
{
	return SECOND(&alarmTimeAndDate);
}


- (int) alarmMinute
{
	return MINUTE(&alarmTimeAndDate);
}


- (int) alarmHour
{
	return HOUR(&alarmTimeAndDate);
}


- (int) alarmDay
{
	return DAY(&alarmTimeAndDate);
}


- (int) alarmMonth
{
	return MONTH(&alarmTimeAndDate);
}


- (int) alarmYear
{
	return YEAR(&alarmTimeAndDate);
}


- (int) alarmWeekday
{
	return WEEKDAY(&alarmTimeAndDate);
}


//----------------------------------------------------------------------------------------------------
//	Accessing Alarm Sound
//----------------------------------------------------------------------------------------------------
- alarmSound: aSound
{
	//  Audible notification of alarm match (in addition to action send to target).

	if (! alarmSound) alarmSound = [[Sound alloc] init];
	[alarmSound copySound: aSound];
	return self;
}


- alarmSound
{
	return alarmSound;
}


//----------------------------------------------------------------------------------------------------
//	Starting and Stopping the Clock
//----------------------------------------------------------------------------------------------------
- startFromSystem: sender
{
	//  Clock starts (and stays synced) with system time.
	
	privateFlags.timerMode = TIMER_REALTIME;
	[self clockTimeAndDate: [self _systemTime]];
	if (! [self isClockRunning]) [self _addTimedEntry];
	return self;
}


- startFromSelf: sender
{
	//  Clock starts from current setting.
	
	privateFlags.timerMode = TIMER_RELATIVE;
	privateFlags.initRelativeTimerMode = YES;
	if (! [self isClockRunning]) [self _addTimedEntry];
	return self;
}


- startFromTime: (struct tm*) theTime
{
	//  Clock sets time and date to theTime and starts timer from this time.
	
	[self clockTimeAndDate: theTime];
	return [self startFromSelf: nil];
}


- stop: sender
{
	if ([self isClockRunning]) [self _removeTimedEntry];
	return self;
}


- activateOnAwake: (int) aFlag;
{
	//  If YES, timer will begin when unarchived.
	
	flags.activateOnAwake = aFlag;
	return self;
}


- (int) activateOnAwake;
{
	return flags.activateOnAwake;
}


//----------------------------------------------------------------------------------------------------
//	Timer Status
//----------------------------------------------------------------------------------------------------
- (BOOL) isClockRunning
{
	return (timedEntry ? YES : NO);
}


//----------------------------------------------------------------------------------------------------
//	Utility Methods
//----------------------------------------------------------------------------------------------------
+ setTimeAndDate:(struct tm*)time :(int)hour :(int)minute :(int)second :(int)month :(int)day :(int)year;
{
	//  Returns by reference the tm struct time.  Struct values are filled with any valid 
	//  arguments (ranges are checked).  Weekday (tm_wday) is calculated based
	//  on the date values passed.  Other struct members use current system defaults.
	
	if (! time) return self;
	memcpy (time,  [self _systemTime], sizeof (struct tm));
	SECOND(time) = (second < 0 || second > 59) ? 0 : second;
	MINUTE(time) = (minute < 0 || minute > 59) ? 0 : minute;
	HOUR(time) = (hour < 0 || hour > 23) ? 0 : hour;
	DAY(time) = (day < 1 || day > 31) ? 1 : day;
	MONTH(time) = (month < 0 || month > 11) ? 0 : month;
	YEAR(time) = (year < 0 || year > 99) ? 0 : year;
	WEEKDAY(time) = [self weekDay: time];
	return self;
}


- setTimeAndDate:(struct tm*)time :(int)hour :(int)minute :(int)second :(int)month :(int)day :(int)year;
{
	return [Clock setTimeAndDate: time :hour :minute :second :month :day :year];
}


- (int) weekDay: (struct tm*) aTime
{
	//  Determine the day of week based on date info in aTime.  Calculated
	//  in Julian format, return value 0 - 6, where 0 = Sunday.
	
	int		day = DAY (aTime);
	int		month = MONTH (aTime) +1;
	int		year = YEAR (aTime) + 1900;
	int		weekDay;
	int 		offset;
	long 	julian;

	if (month <= 2) { year--; month += 12;}
	offset = (2 - (year / 100)) + ((year / 100) / 4);
	julian = (365.25 * year);
	julian += (long) (30.6001 * (month +1));
	julian += (long) day;
	julian += 1720994L;
	julian += (long) offset;

	weekDay = (int) ((julian + 2) % 7);
	return (weekDay < 0 || weekDay > 6 ? 0 : weekDay);
}


//----------------------------------------------------------------------------------------------------
//	Display Methods
//----------------------------------------------------------------------------------------------------
- showClockTime: sender
{
	//  Shows current time and date setting. Will not disturb current timer.
	
	displayTimeAndDate = &clockTimeAndDate;
	privateFlags.displayMode = DISPLAY_OTHER;
	[self update];
	return self;
}


- showSystemTime: sender
{
	//   Displays system time. Will not disturb current timer.
	
	displayTimeAndDate = [self _systemTime];
	privateFlags.displayMode = DISPLAY_OTHER;
	[self update];
	return self;
}


- showAlarmTime: sender
{
	//  Displays current alarm time and date. Will not disturb current timer.
	
	displayTimeAndDate = &alarmTimeAndDate;
	privateFlags.displayMode = DISPLAY_ALARM;
	[self update];
	return self;
}


- showTime: (struct tm*) aTime
{
	//   Displays system time. Will not disturb current timer.
	
	displayTimeAndDate = aTime;
	privateFlags.displayMode = DISPLAY_OTHER;
	[self update];
	return self;
}


- resumeClockDisplay: sender
{
	//  Invoke this to resume display of current clock time.
	
	displayTimeAndDate = &clockTimeAndDate;
	privateFlags.displayMode = DISPLAY_CLOCK;
	[self update];
	return self;
}	


//----------------------------------------------------------------------------------------------------
//	IB Action Methods
//----------------------------------------------------------------------------------------------------
- takeDisplayEnabledFlagFrom: sender;
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self setDisplayEnabled: [sender state]];
	return self;
}


- takeWantsSecondsFlagFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self wantsSeconds: [sender state]];
	return self;
}


- takeWantsDateFlagFrom: sender;
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self wantsDate: [sender state]];
	return self;
}


- takeWantsMilitaryTimeFlagFrom: sender;
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self wantsMilitaryTime: [sender state]];
	return self;
}


- takeIsDraggableFlagFrom: sender;
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self isDraggable: [sender state]];
	return self;
}


- takeWillAcceptDropFlagFrom: sender;
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self willAcceptDrop: [sender state]];
	return self;
}


- takeEnableAlarmFlagFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self enableAlarm: [sender state]];
	return self;
}


- takeWantsAlarmIndicatorFlagFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (state)]) [self wantsAlarmIndicator: [sender state]];
	return self;
}


- takeBackgroundColorFrom: sender
{
	if ([sender respondsTo: @selector (color)]) [self backgroundColor: [sender color]];
	return self;
}


- takeClockSecondIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self clockSecond: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeClockMinuteIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self clockMinute: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeClockHourIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self clockHour: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeClockDayIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self clockDay: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeClockMonthIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self clockMonth: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeClockYearIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self clockYear: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeClockWeekdayIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self clockWeekday: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmSecondIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self alarmSecond: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmMinuteIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self alarmMinute: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmHourIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self alarmHour: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmDayIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self alarmDay: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmMonthIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self alarmMonth: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmYearIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self alarmYear: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmWeekdayIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (intValue)]) [self alarmWeekday: [sender intValue]];
	[[self _nextText: sender] update];
	return self;
}


- takeAlarmModeIntValueFrom: sender
{
	if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
	if ([sender respondsTo: @selector (tag)]) privateFlags.alarmMode = [sender tag];
	return self;
}


- incrementClockSecond: sender
{
	[self clockSecond: [self clockSecond] + 1];
	[self showClockTime: nil];
	return self;
}


- incrementClockMinute: sender
{
	[self clockMinute: [self clockMinute] + 1];
	[self showClockTime: nil];
	return self;
}


- incrementClockHour: sender
{
	[self clockHour: [self clockHour] + 1];
	[self showClockTime: nil];
	return self;
}


- incrementClockDay: sender
{
	[self clockDay: [self clockDay] + 1];
	[self showClockTime: nil];
	return self;
}


- incrementClockMonth: sender
{
	[self clockMonth: [self clockMonth] + 1];
	[self showClockTime: nil];
	return self;
}


- incrementClockYear: sender
{
	[self clockYear: [self clockYear] + 1];
	[self showClockTime: nil];
	return self;
}


- incrementClockWeekday: sender
{
	[self clockWeekday: [self clockWeekday] + 1];
	[self showClockTime: nil];
	return self;
}


- decrementClockSecond: sender
{
	[self clockSecond: [self clockSecond] - 1];
	[self showClockTime: nil];
	return self;
}


- decrementClockMinute: sender
{
	[self clockMinute: [self clockMinute] - 1];
	[self showClockTime: nil];
	return self;
}


- decrementClockHour: sender
{
	[self clockHour: [self clockHour] - 1];
	[self showClockTime: nil];
	return self;
}


- decrementClockDay: sender
{
	[self clockDay: [self clockDay] - 1];
	[self showClockTime: nil];
	return self;
}


- decrementClockMonth: sender
{
	[self clockMonth: [self clockMonth] - 1];
	[self showClockTime: nil];
	return self;
}


- decrementClockYear: sender
{
	[self clockYear: [self clockYear] - 1];
	[self showClockTime: nil];
	return self;
}


- decrementClockWeekday: sender
{
	[self clockWeekday: [self clockWeekday] - 1];
	[self showClockTime: nil];
	return self;
}



- incrementAlarmSecond: sender
{
	[self alarmSecond: [self alarmSecond] + 1];
	[self showAlarmTime: nil];
	return self;
}


- incrementAlarmMinute: sender
{
	[self alarmMinute: [self alarmMinute] + 1];
	[self showAlarmTime: nil];
	return self;
}


- incrementAlarmHour: sender
{
	[self alarmHour: [self alarmHour] + 1];
	[self showAlarmTime: nil];
	return self;
}


- incrementAlarmDay: sender
{
	[self alarmDay: [self alarmDay] + 1];
	[self showAlarmTime: nil];
	return self;
}


- incrementAlarmMonth: sender
{
	[self alarmMonth: [self alarmMonth] + 1];
	[self showAlarmTime: nil];
	return self;
}


- incrementAlarmYear: sender
{
	[self alarmYear: [self alarmYear] + 1];
	[self showAlarmTime: nil];
	return self;
}


- incrementAlarmWeekday: sender
{
	[self alarmWeekday: [self alarmWeekday] + 1];
	[self showAlarmTime: nil];
	return self;
}


- decrementAlarmSecond: sender
{
	[self alarmSecond: [self alarmSecond] - 1];
	[self showAlarmTime: nil];
	return self;
}


- decrementAlarmMinute: sender
{
	[self alarmMinute: [self alarmMinute] - 1];
	[self showAlarmTime: nil];
	return self;
}


- decrementAlarmHour: sender
{
	[self alarmHour: [self alarmHour] - 1];
	[self showAlarmTime: nil];
	return self;
}


- decrementAlarmDay: sender
{
	[self alarmDay: [self alarmDay] - 1];
	[self showAlarmTime: nil];
	return self;
}


- decrementAlarmMonth: sender
{
	[self alarmMonth: [self alarmMonth] - 1];
	[self showAlarmTime: nil];
	return self;
}


- decrementAlarmYear: sender
{
	[self alarmYear: [self alarmYear] - 1];
	[self showAlarmTime: nil];
	return self;
}


- decrementAlarmWeekday: sender
{
	[self alarmWeekday: [self alarmWeekday] - 1];
	[self showAlarmTime: nil];
	return self;
}


//----------------------------------------------------------------------------------------------------
//	Draw Methods
//----------------------------------------------------------------------------------------------------
-drawSelf: (const NXRect *)rects :(int)rectCount
{
	//  Manage display as appropriate.  Subclasses implement drawing
	//  specific to their graphical representation.

	if (flags.wantsDisplay)
		{
		[self drawBackground];
		[self drawClockFace];
		[self drawAlarmIndicator];
		[self drawTime];
		[self drawDate];
		}

	return self;
}


- drawBackground
{
	//  Fill background with image (scaled) or background color. 	
	if (privateFlags.backgroundIsColor)
		{
		NXSetColor (backgroundColor);
		NXRectFill (&bounds);
		return self;
		}
		
	[backgroundImage setSize: &bounds.size];
	[backgroundImage composite: NX_SOVER toPoint: &bounds.origin];

	return self;
}
		

- drawClockFace
{
	//  Center clockFace image in bounds rect.
	
	NXPoint	compositePoint = [self _compositePointForImage:clockFace];
	[clockFace composite: NX_SOVER toPoint: &compositePoint];
	return self;
}


- drawTime
{
	return self;
}


- drawDate
{
	return self;
}


- drawAlarmIndicator
{
	//  Visible indicator that alarm is set and enabled.
	
	if (flags.isAlarmEnabled && flags.wantsAlarmIndicator) 
		{
    		id font = [Font newFont:"Times-Roman" size:12 style:0 matrix:NX_IDENTITYMATRIX];
		NXSetColor (NX_COLORRED);
		[font set];
		PSmoveto (2.0, 0.0);
		PSshow ("·");
		[font free];
		}
		
	return self;
}


//----------------------------------------------------------------------------------------------------
//	Dragging Support
//----------------------------------------------------------------------------------------------------
- isDraggable: (BOOL) aFlag
{
	flags.isDraggable = aFlag;
	return self;
}


- willAcceptDrop: (BOOL) aFlag;
{
	const char*	dragTypes[] = {ClockPboardType, NULL};

	flags.willAcceptDrop = aFlag;
	
	if (flags.willAcceptDrop)
		[self registerForDraggedTypes:dragTypes count:1];
	else
		[self unregisterDraggedTypes];
		
	return self;
}


- (BOOL) isDraggable
{
	return flags.isDraggable;
}


- (BOOL) willAcceptDrop
{
	return flags.willAcceptDrop;
}


- (BOOL)acceptsFirstMouse
{
    	return flags.isDraggable;
}


- mouseDown: (NXEvent*) theEvent
{
	//  If draggable, initiate drag sequence on mouse-drag...
	
	int			originalEventMask;
	NXEvent		originalEvent = *theEvent;
	NXEvent		nextEvent;

	if (! flags.isDraggable) return self;

    	[NXApp preventWindowOrdering];

	originalEventMask = [window addToEventMask: NX_LMOUSEDRAGGEDMASK];

	nextEvent = *( [NXApp getNextEvent: (NX_LMOUSEUPMASK | 
				 				        NX_LMOUSEDRAGGEDMASK)] );

	switch (nextEvent.type)
		{
		case NX_LMOUSEDRAGGED:
			[self beginDragOperationFor: &originalEvent  nextEvent: &nextEvent];
			
		case NX_LMOUSEUP:
		default:
			break;
		}
	
	[window setEventMask: originalEventMask];
    	return self;
}


- beginDragOperationFor: (NXEvent*) originalEvent  nextEvent: (NXEvent*) nextEvent
{
	//  Begin the drag sequence.  
	
	NXPoint	startingPoint = { 0.0, 0.0 };
	id		dragImage = [self dragImage];	
	id 		pasteboard = [Pasteboard newName: NXDragPboard];
    	
	[self writeToPasteboard:pasteboard];
	
	[self dragImage: dragImage
		at: &startingPoint
		offset: &startingPoint 
		event: originalEvent 
		pasteboard: pasteboard 
		source: self
		slideBack: YES];
		
	if (dragImage) [dragImage free];
	if (pasteboard) [pasteboard free];

	return self;
}
			

- (NXImage*) dragImage
{
	id	bitmap;
	id	image = [[NXImage alloc] init];

	[self lockFocus];
	bitmap = [[NXBitmapImageRep alloc] initData: NULL fromRect: &bounds];
	[self lockFocus];
	[image useRepresentation: bitmap];
	return image;
}


- (BOOL) shouldDelayWindowOrderingForEvent: (NXEvent*) theEvent
{
 	//  This allows you to drag a partially obscure object without making 
	//  the source window become main, and without making the source 
	//  application the current app.

    	return YES;
}


//----------------------------------------------------------------------------------------------------
//	Dragging Source Protocol
//----------------------------------------------------------------------------------------------------
- (NXDragOperation) draggingSourceOperationMaskForLocal: (BOOL)aFlag;
{	
	return NX_DragOperationAll;
}


//----------------------------------------------------------------------------------------------------
//	Dragging Destination Protocol
//----------------------------------------------------------------------------------------------------
- (NXDragOperation)draggingEntered:dragSource
{
    	return [self draggingUpdated:dragSource];
}


- (NXDragOperation)draggingUpdated:dragSource
{
	if ([dragSource draggingSource] == self) return NX_DragOperationNone; 

	switch ([dragSource draggingSourceOperationMask])
		{
		case NX_DragOperationCopy:
			return NX_DragOperationCopy;
		case NX_DragOperationLink:
			return NX_DragOperationLink;
		case NX_DragOperationGeneric:
		case NX_DragOperationAll:
		default:
			return NX_DragOperationGeneric;
		}
}


- (BOOL)prepareForDragOperation:dragSource
{
	return YES;
}


- (BOOL)performDragOperation:dragSource
{
	id pasteboardClock = [self readFromPasteboard: [dragSource draggingPasteboard]];

	switch ([dragSource draggingSourceOperationMask])
		{
		case NX_DragOperationCopy:
			[self dragOperationCopy: pasteboardClock];
			break;
		case NX_DragOperationLink:
			[self dragOperationLink: pasteboardClock];
			break;
		case NX_DragOperationGeneric:
		case NX_DragOperationAll:
		default:
			[self dragOperationGeneric: pasteboardClock];
			break;
		}

	[pasteboardClock free];
	return YES;
}


- concludeDragOperation:dragSource
{
	return self;
}


//----------------------------------------------------------------------------------------------------
//	Clock Drag Operations
//----------------------------------------------------------------------------------------------------
- dragOperationCopy: pasteboardClock
{
	//  User is holding Alternate key down while dropping, copy
	//  only Alarm information.
	
	[self alarmTimeAndDate: [pasteboardClock alarmTimeAndDate]];
	[self enableAlarm: [pasteboardClock isAlarmEnabled]];
	[self wantsAlarmIndicator: [pasteboardClock wantsAlarmIndicator]];
	privateFlags.alarmMode = [pasteboardClock _alarmMode];
	privateFlags.alarmSleep = NO;
	[self update];
	return self;
}

	
- dragOperationLink: pasteboardClock
{
	//  User is holding the Control key down while dropping, copy 
	//  only Clock information.

	[self clockTimeAndDate: [pasteboardClock clockTimeAndDate]];

	if ([pasteboardClock activateOnAwake])
		if ([pasteboardClock _timerMode] == TIMER_REALTIME)
			[self startFromSystem: nil];
		else
			{
			[self startFromSelf: nil];
			privateFlags.initRelativeTimerMode = NO;
			previousClock = [pasteboardClock _previousClock];
			}
	else
		[self stop: nil];
			
	[self update];
	return self;
}


- dragOperationGeneric: pasteboardClock
{
	//  User is holding Command (or no) key down while dropping, copy
	//  both Clock and Alarm information.
	
	[self dragOperationCopy: pasteboardClock];
	[self dragOperationLink: pasteboardClock];
	[self update];
	return self;
}


//----------------------------------------------------------------------------------------------------
//	Pasteboard Methods
//----------------------------------------------------------------------------------------------------
- writeToPasteboard: aPasteboard
{
	//  Only tricky thing here is to make sure the pasteboard object will not 
	//  begin a timed entry in the awake method when it is readFromPasteboard.
		
	int 				length; 
	int				maxLength;
	char*			data;
	NXStream*		aStream = NXOpenMemory (NULL, 0, NX_WRITEONLY);
	NXTypedStream*	aTypedStream = NXOpenTypedStream (aStream, NX_WRITEONLY);
	
	if ([self isClockRunning]) flags.activateOnAwake = AWAKE_PREVENT;

	NXWriteRootObject (aTypedStream, self);
	NXGetMemoryBuffer(aStream, &data, &length, &maxLength);
    	[aPasteboard declareTypes: &ClockPboardType num:1 owner: self];
	[aPasteboard writeType:ClockPboardType data:data length:length];

	flags.activateOnAwake = [self isClockRunning];

	NXCloseTypedStream (aTypedStream);
	NXClose (aStream);
	return self;
}


- readFromPasteboard: aPasteboard
{
	NXStream*		aStream = [aPasteboard readTypeToStream:ClockPboardType];
	NXTypedStream*	aTypedStream = NXOpenTypedStream (aStream, NX_READONLY);

	id  pasteboardClock = NXReadObject (aTypedStream);

	NXCloseTypedStream (aTypedStream);
	NXClose (aStream);
	return pasteboardClock;
}


//----------------------------------------------------------------------------------------------------
//	Archiving Methods
//----------------------------------------------------------------------------------------------------
- read: (NXTypedStream*) stream
{
	[super read: stream];
	NXReadType (stream, "{iiiiiii}", &clockTimeAndDate);
	NXReadType (stream, "{iiiiiii}", &alarmTimeAndDate);
	NXReadType (stream, "s", (short*)&flags);
	NXReadType (stream, "s", (short*)&privateFlags);
	backgroundColor = NXReadColor (stream);
	NXReadType (stream, "@", &backgroundImage);
	NXReadType (stream, "@", &clockFace);
	NXReadType (stream, "@", &alarmSound);
	NXReadType (stream, "i", &previousClock);
	return self;
}


- write: (NXTypedStream*) stream
{
	[super write: stream];
	NXWriteType (stream, "{iiiiiii}", &clockTimeAndDate);
	NXWriteType (stream, "{iiiiiii}", &alarmTimeAndDate);
	NXWriteType (stream, "s", (short*)&flags);
	NXWriteType (stream, "s", (short*)&privateFlags);
	NXWriteColor (stream, backgroundColor);
	NXWriteType (stream, "@", &backgroundImage);
	NXWriteType (stream, "@", &clockFace);
	NXWriteType (stream, "@", &alarmSound);
	NXWriteType (stream, "i", &previousClock);
	return self;
}


- awake
{
	//  Clock awakes displaying clock time and starts timer if appropriate.
	
	[self willAcceptDrop: flags.willAcceptDrop];
	
	privateFlags.displayMode = DISPLAY_CLOCK;
	displayTimeAndDate = &clockTimeAndDate;

	if (flags.activateOnAwake != AWAKE_RUNNING) return self;

	if (privateFlags.timerMode == TIMER_REALTIME)
		[self startFromSystem: nil];
	else
		[self startFromSelf: nil];

	return self;
}
	

@end