ftp.nice.ch/pub/next/tools/workspace/Cassandra.1.7a.s.tar.gz#/Cassandra/AddEvent.m

This is AddEvent.m in view mode; [Download] [Up]

//
// AddEvent.m
// Copyright (c) 1989, 1990, 1991 by Jiro Nakamura 
// All rights reserved
//
// Handles the window that allows the user to add events.
// Also is the superclass of EventEdit, which lets the user edit events
//
//	by Jiro Nakamura (jiro@shaman.com)
//
// RCS Information
// Revision Number->	$Revision: 2.15 $
// Last Revised->	$Date: 91/11/01 17:23:32 $
//
static char rcsid[] = "$Id: AddEvent.m,v 2.15 91/11/01 17:23:32 jiro Exp Locker: jiro $";

#import "AddEvent.h"
#import "Global.h"
#import "Cassandra.h"
#import "calendar.h"
#import <libc.h>
#import <strings.h>			// for rindex() and strcpy() 
#import <sys/file.h>			// for access()
#import <appkit/Application.h>
#import <appkit/Button.h>
#import <appkit/Form.h>
#import <appkit/Matrix.h>
#import <appkit/Panel.h>       		// for NXRunAlertPanel 
#import <appkit/OpenPanel.h>		// for OpenPanel


#define	RADIO_WEEK	0
#define	RADIO_MON	1
#define	RADIO_MDAY	2
#define	RADIO_YEAR	3
#define	RADIO_HOUR	4
#define	RADIO_MIN	5

static char *frequencyChar[4] = {"day","week","month","year"};
static char *alarmExtensions[] = ALARMEXTENSIONS;

@implementation AddEvent

/* The main reason for the following method is just to avoid warnings
 *     about rcsid being unreferenced...   Garance/Jul 28/94
 */
- (const char *) returnRcsid
{
     return rcsid;
}

- open: sender
{
	ev = [Event newAt: [global eventFile]];
	[self resetDefaultValues : self];
	return self;
}

- close
{	
	if( [self isDocEdited] && [self isVisible])
		{		
		#ifdef DEBUG
			fprintf(stderr,"Add/Edit Event window about to close." 
				"Checking save.\n");
		#endif
	
	
		 if (NXRunAlertPanel("Changes not saved",
		 	"You have made changes to this window. "
			"Do you really want to throw them out?",
			"Dump them","Cancel",NULL) == 0 )
				return self;
		}
	
	[self setDocEdited: FALSE];
	
	[super close];
	[ev free];
	return self;
}


- startOver:sender
{
	[self resetDefaultValues: self];
	return self;
}

- resetDefaultValues:sender
{
	static struct tm time;
	
	time = *timeNow();
	
	[ev setTime:		&time];
	[ev setSec:		0];
	[ev setSnoozeInt:	0];
	[ev setSnoozeNo:		0];
	[ev setPriority:	50];
	[ev setDestroy:		1];	// Destroy not supported after v0.92
	[ev setAnniversary:	0];
	[ev setMessage:		"Empty Message"];
	[ev setAlarmSound:	[global alarmSound]];
	[ev setPlayAlarm:	[global playAlarm]];
	[ev setShowMessage:	TRUE];
	[self displayValues:	sender];
	
	[self displayValues: self];
		
	[monthForm selectTextAt:0];
	[dateRadioMatrix	selectCellAt: 0: RADIO_MDAY];
	
	return self;
}

- displayValues:sender
{
	int temp, temp2;

	[self displayDate: self];
	
	if( [ev priority] < 10)
		[priorityRadioMatrix	selectCellAt:	0: 0];
	else 
		{
		if( [ev priority] <= 90)
			[priorityRadioMatrix	selectCellAt: 0:1];
		else
			[priorityRadioMatrix	selectCellAt: 0:2];
		}
	
		
	[messageForm	setStringValue: [ev message]	at:0];
	
	[messageButton	setState:	[ev showMessage]];
	[self message: self];
	
	[alarmSoundForm		setStringValue: [ev alarmSound]	at:0];
	
	switch( [ev alarmAction] )
		{
		case 1:	// playSound
		case 2: // playAndDeleteSound
			[alarmSoundRadioMatrix	selectCellAt: 0 : 0];	
			break;	
		case 3: // runCommand
			[alarmSoundRadioMatrix	selectCellAt: 1 : 0];	
			break;
		
		case 0:	// nothing
		default:
			[alarmSoundRadioMatrix	selectCellAt: 2 : 0];	
			break;
		}
		


	
	[self sound: self];
	
		
	if( [ev snoozeNo] > 0)
		{
		[snoozeIntForm	setIntValue: 	[ev snoozeInt]	at:0];
		[snoozeNoForm	setIntValue:	[ev snoozeNo]	at:0];
		[snoozeButton		setState: 	TRUE];
		}
	else
		{
		[snoozeIntForm	setIntValue: 	5	at:0];
		[snoozeNoForm	setIntValue:	6	at:0];
		[snoozeButton		setState:	FALSE];
		}
	[self	snooze: self];


	temp = 	[ev anniversary] % 100;
	temp2 =	[ev anniversary] / 100 - 1;
	if( temp2 <= 0)
		temp2 = 0;
				
	[anniversaryForm 	setIntValue: ((temp == 0) ? 1 : temp) at:0];
	[anniversaryRadioMatrix	selectCellAt: 0 : temp2];
	[anniversaryButton	setState: 	( [ev anniversary] > 0)];
				
	[self anniversary:self];
	[self makeKeyAndOrderFront : self];
	
	// The window is new
	[self setDocEdited:	FALSE];

	return self;
}



- displayDate: sender
{
	int temp;
	char min[5];
	extern const char *shortMonths[12];
	
	if( [global militaryTime])
		{
		[hourForm	setIntValue:	[ev hour]];
		[amPmButton	setEnabled: FALSE];
		[amPmButton 	setState:	AM];
		}
	else
		{
		temp = [ev hour];
		if( temp > 12 )
			temp -= 12;
		if( temp == 0 )
			temp = 12;
		[hourForm 	setIntValue: 	temp   	at:0];
		[amPmButton	setEnabled:	TRUE];
		if( [ev hour] >= 12)
			[amPmButton 	setState: PM];
		else
			[amPmButton 	setState: AM];
		}
		
	sprintf(min,"%02d",[ev min]);
	[minuteForm 	setStringValue: min	at:0];
	[dayForm 	setIntValue: 	[ev mday]	at:0];
	[monthForm 	setStringValue: shortMonths[([ev mon])]
			 	at:	0];	
	[yearForm 	setIntValue 	: ([ev year]+ 1900)	at:0];
	[self updateTestDate : [ev time]];
	return self;
}

- dateUp: sender
{
	struct tm *displayTime;

	if( (displayTime = [self getDate: self]) == NULL)
		return self;
	
	switch( [dateRadioMatrix selectedCol])
		{
		case RADIO_WEEK:
			displayTime->tm_mday += 7;
			break;
		case RADIO_MON:
			displayTime->tm_mon ++;
			break;
		case RADIO_MDAY:
			displayTime->tm_mday ++;
			break;
		case RADIO_YEAR:
			displayTime->tm_year ++;
			break;
		case RADIO_HOUR:
			displayTime->tm_hour ++;
			break;
		case RADIO_MIN:
			displayTime->tm_min ++;
			break;
		}

	fixTmStructure( displayTime);
	[ev setTime: displayTime];
	[self displayDate: self];
	[self setDocEdited:	TRUE];
	return self;
}

- dateDown: sender
{
	struct tm *displayTime;

	if( (displayTime = [self getDate: self]) == NULL)
		return self;
	
	switch( [dateRadioMatrix selectedCol])
		{
		case RADIO_WEEK:
			displayTime->tm_mday -= 7;
			break;
		case RADIO_MON:
			displayTime->tm_mon --;
			break;
		case RADIO_MDAY:
			displayTime->tm_mday --;
			break;
		case RADIO_YEAR:
			displayTime->tm_year --;
			break;
		case RADIO_HOUR:
			displayTime->tm_hour --;
			break;
		case RADIO_MIN:
			displayTime->tm_min --;
			break;
		}

	fixTmStructure( displayTime);
	[ev setTime: displayTime];
	[self displayDate: self];
	[self setDocEdited:	TRUE];
	return self;
}
			
			
- anniversary:sender
{
	BOOL aFlag;
	
	#ifdef DEBUG
		fprintf(stderr,"Anniversary button pressed. State = %d\n",
			[anniversaryButton state]);
	#endif

	aFlag = [anniversaryButton state];
	
	[anniversaryRadioMatrix	setEnabled: aFlag];
	[anniversaryForm	setEnabled: aFlag];
	[everyTextField		setEnabled: aFlag];
	[anniversaryUpButton	setEnabled: aFlag];
	[anniversaryUpButton	setIcon:
		(aFlag?RIGHTARROW:DISABLEDRIGHTARROW)];
	[anniversaryDownButton	setEnabled: aFlag];
	[anniversaryDownButton	setIcon:
		(aFlag?LEFTARROW:DISABLEDLEFTARROW)];
			
	if( aFlag )
		[anniversaryForm	selectTextAt: 0];
			
	[self	anniversaryFinished: self];
    	return self;
}

- anniversaryUp: sender
{
	[anniversaryForm	setIntValue: 
		([anniversaryForm intValueAt:0] +1) at:0];
	[self anniversaryFinished: self];
	return self;
}

- anniversaryDown: sender
{
	int temp;

	temp = [anniversaryForm	intValueAt: 0] - 1;
	
	if( temp > 1)
		[anniversaryForm	setIntValue: temp at:0];
	else
		[anniversaryForm	setIntValue: 1 at:0];
		
	[self anniversaryFinished: self];	
	return self;
}


- snooze: sender
{
	BOOL aFlag;		// the state of the snooze button

	#ifdef DEBUG
		fprintf(stderr,
			"Snooze button pressed. State = %d\n",
			[snoozeButton state]);
	#endif
	
	aFlag = [snoozeButton state];
	
	[snoozeNoForm		setEnabled: aFlag];
	[snoozeIntForm		setEnabled: aFlag];
	[snoozeTimesTextField	setEnabled: aFlag];
	[snoozeMinutesTextField	setEnabled: aFlag];
	
	[snoozeIntUpButton	setEnabled: aFlag];
	[snoozeIntUpButton	setIcon:
		aFlag?RIGHTARROW:DISABLEDRIGHTARROW];
	[snoozeIntDownButton	setEnabled: aFlag];
	[snoozeIntDownButton	setIcon:
		aFlag?LEFTARROW:DISABLEDLEFTARROW];
	[snoozeNoUpButton	setEnabled: aFlag];
	[snoozeNoUpButton	setIcon:
		aFlag?RIGHTARROW:DISABLEDRIGHTARROW];
	[snoozeNoDownButton	setEnabled: aFlag];
	[snoozeNoDownButton	setIcon:
		aFlag?LEFTARROW:DISABLEDLEFTARROW];
	
	if( aFlag )
		[snoozeIntForm	selectTextAt:0];
			
	[self snoozeFinished: self];
	return self;
}

- snoozeIntUp: sender
{
	[snoozeIntForm	setIntValue: 
		([snoozeIntForm	intValueAt: 0] + 1) at:0];
	[self snoozeFinished: self];
	return self;

}

- snoozeIntDown: sender
{
	int temp;

	temp = [snoozeIntForm	intValueAt: 0] - 1;
	if( temp > 1)
		[snoozeIntForm	setIntValue: temp at:0];
	else
		[snoozeIntForm	setIntValue: 1 at:0];
	
	[self snoozeFinished: self];
	return self;
}

- snoozeNoUp: sender
{
	[snoozeNoForm	setIntValue: 
		([snoozeNoForm	intValueAt: 0] + 1) at:0];
	[self snoozeFinished: self];
	return self;
}

- snoozeNoDown: sender
{
	int temp;

	temp = [snoozeNoForm	intValueAt: 0] - 1;
	if( temp > 1)
		[snoozeNoForm	setIntValue: temp at:0];
	else
		[snoozeNoForm	setIntValue: 1 at:0];
	
	[self snoozeFinished: self];
	return self;
}

- snoozeFinished: sender
{
	static char buf[50];
	int sInt, sNo;

	// The window has changed
	[self setDocEdited:	TRUE];

	if( [snoozeButton state])
		{
		sInt = [snoozeIntForm	intValueAt: 0];
		sNo  = [snoozeNoForm	intValueAt: 0];
		
		sprintf(buf, "time%s",(sNo == 1)?"":"s");
		[snoozeTimesTextField	setStringValue: buf];
		
		sprintf(buf, "minute%s",(sInt == 1)?"":"s");
		[snoozeMinutesTextField	setStringValue: buf];
			
		sprintf(buf,"Snooze alarm set every %d minute%s "
			"for the next %d minute%s.",
			sInt,
			(sInt == 1)?"":"s",
			(sInt * sNo),
			((sInt * sNo)  == 1)?"":"s"
			);
		[testSnoozeTextField setStringValue: buf];
		}
	else
		[testSnoozeTextField setStringValue:
			"The snooze function is not enabled."];

	[self selectNextText: sender];
	return self;
}


- message: sender
{
	[messageForm	setEnabled:	[messageButton	state]];
	return self;
}

- messageFinished: sender
{
	// The window has changed
	[self setDocEdited:	TRUE];
	return self;
}


- sound: sender
{
	int mode;		// state of alarmSound matrix

	mode = [alarmSoundRadioMatrix selectedRow];
	
	[alarmSoundForm	setEnabled:	mode != 2];
	[playButton	setEnabled:	mode == 0];
	[setButton	setEnabled:	mode == 0];
	[stopButton	setEnabled:	FALSE];
	
	if( mode !=2)
		[alarmSoundForm	selectTextAt: 0];
	return self;
}

- soundFinished: sender
{
	// The window has changed
	[self setDocEdited:	TRUE];

	if( [alarmSoundRadioMatrix selectedRow] == 0)
		[self checkSound: self];
	return self;
}

- playSound:sender
{
	int error;
	
	// If we are given a path name for an alarm sound, use the pathname
	// in newFromSoundFile. Otherwise, we will have to go look for it
	// using findSoundFor.
	if( index((char *) [alarmSoundForm stringValueAt:0], '/') 
				== (char *) 0)
		soundfile = [Sound findSoundFor:  
			(char *) [alarmSoundForm stringValueAt:0]];
	else
		soundfile = [[Sound alloc] initFromSoundfile: 
			(char *) [alarmSoundForm stringValueAt:0]];
		
	if( soundfile != nil)
			{
			[soundfile setDelegate: self];
			// From 1.0 Docs, Sound Kit, pp22-498
			[Sound getVolume: &oldVolumeLeft:&oldVolumeRight];
			if( [global volume] >= 0)
				[Sound setVolume: [global volume]
						: [global volume]];

			error = [soundfile play];
			if( error == 0)
				{
				[stopButton 	setEnabled	: TRUE];
				[playButton 	setEnabled	: TRUE];
				 }
			else
				{
				char errorString[200];
				// Restore old volume
				[Sound	setVolume: oldVolumeLeft:
						 oldVolumeRight];

				sprintf(errorString,
					"Error #%d (%s) occured while "
					"trying to play the sound <%s>.\n", 
					error, SNDSoundError(error),
					[alarmSoundForm stringValueAt:0]);
					
				NXRunAlertPanel("Alarm Error",errorString, 
						"Oh well...",NULL,NULL);
				}
			}
		else
			{
			NXRunAlertPanel("Sound not found",
				"I couldn't find that sound.", 
				"Oh well...",NULL,NULL);
			[alarmSoundForm	selectText:self];
			}
	
	return self;
}

- checkSound:sender
{

	if( [alarmSoundRadioMatrix selectedRow] != 0 )
		{
		[self selectNextText:sender];
		return self;
		}
		
		
	if( 	([Sound findSoundFor:  
			(char *) [alarmSoundForm stringValueAt:0]] == nil)  &&
		( access( [alarmSoundForm stringValueAt:0], R_OK) == -1))
		{
		NXRunAlertPanel("Sound not found",
				"I couldn't find that sound. "
				"Please double check.",
				"OK",NULL,NULL);
		[alarmSoundForm	selectText:self];
		}
	else
		[self selectNextText:sender];
	return self;
}


- stopSound:sender
{
	[soundfile stop];
   	 return self;
}

- willPlay : sender
{
	#ifdef DEBUG
		fprintf(stderr,"Alarm sound starting.\n");
	#endif
	
	[playButton setEnabled: FALSE];
	return self;
}

- hadError: sender
{
	char errorString[200];
	int error = [[sender soundBeingProcessed] processingError];	
	
	sprintf(errorString,
		"Error #%d (%s) occured while playing the sound <%s>.\n", 
		error, SNDSoundError(error),
		[alarmSoundForm stringValueAt:0]);
					
	NXRunAlertPanel("Alarm Error",errorString, 
			"Oh well...",NULL,NULL);
		
	[stopButton setEnabled: FALSE];
	if( [alarmSoundRadioMatrix selectedRow] == 0)
		[playButton	setEnabled:	TRUE];
	else
		[playButton	setEnabled:	FALSE];
		
	// Restore old volume
	[Sound	setVolume: oldVolumeLeft: oldVolumeRight];
	return self;
}

- didPlay:sender
{
	[soundfile free];
	[stopButton setEnabled: FALSE];
		
	if( [alarmSoundRadioMatrix selectedRow] == 0)
		[playButton	setEnabled:	TRUE];
	else
		[playButton	setEnabled:	FALSE];

	// Restore old volume
	[Sound	setVolume: oldVolumeLeft: oldVolumeRight];
	
	return self;
}


- insertEventAndClose: sender
{
	#ifdef DEBUG
		fprintf(stderr,"InsertEventAndClose: %x is the flag.\n",
			[NXApp currentEvent]->flags);
	#endif
	
	if( [self insertEvent:self] == nil)		// user aborted
		return self;
	
	if( !( ([NXApp currentEvent]->flags) & NX_ALTERNATEMASK))	
		[self close];
	[cassandra queueDidChange:self];
	return self;
}
	
- insertEvent:sender
{
	struct tm time, *timePointer;
	int vAnniv;
	
	time = *timeNow();
	
	[ev setMessage:		(char *) [messageForm 	stringValueAt:0]];
	[ev setShowMessage:	[messageButton		state]];
	switch( [alarmSoundRadioMatrix selectedRow] )
		{
		case 0:	// sound
			[ev setAlarmAction: 1];
			break;
		case 1: // runCommand
			[ev setAlarmAction: 3];
			break;
		case 2: // nada
			[ev setAlarmAction: 0];
		break;
		}

	[ev setAlarmSound:	(char *) [alarmSoundForm stringValueAt:0]];

	if( (timePointer = [self getDate: self]) == NULL)
		return nil;
	 
	[ev setTime:	timePointer];
	
	if( [anniversaryForm intValueAt:0] > 99)
		{
		NXRunAlertPanel("Anniversary Overload",
			"Due to a design error that cannot be fixed "
			"until a future version, you cannot have a reoccuring "
			"event of more than 99 intervals.","OK",NULL,NULL);
		[anniversaryForm selectTextAt: 0];
		return nil;
		}
	vAnniv = ([anniversaryRadioMatrix selectedCol] +1 ) * 100;	
	[ev setAnniversary: 	([anniversaryButton state] 
				? ([anniversaryForm intValueAt:0] + vAnniv)	
				: 0) ];
	
	[ev setDestroy:		1];	// Destroy not supported after v0.9.2
	
	[ev setSnoozeNo:		([snoozeButton state]
				?[snoozeNoForm intValueAt:0]
				:0)];
	[ev setSnoozeInt: 	([snoozeButton state]
				?[snoozeIntForm intValueAt:0]
				:0)];
	[ev setPriority: 	[priorityRadioMatrix selectedCol]*50];
	
	// If the user tries to put in an event with a date that has 	
	// already past, inform them of the problem */
	if(  timeCompare(  [ev time], &time ) == -1)
		if(  NXRunAlertPanel("Insert Warning",
			"The date you have typed in has already passed. "
			"Are you sure you want to insert it?",
			"Insert","Cancel",NULL) == 0)
			return nil;
		
	[ev insertEvent];

	// The window has been saved
	[self setDocEdited:	FALSE];

	return self;
}


- setSound:sender
{
	OpenPanel *myOpenPanel;
	char tempDir[128], *tempFile;

	myOpenPanel = [OpenPanel new];
	
	if( index([ev alarmSound], '/') == (char *) 0)	// If the present isn't
		[ev setAlarmSound: ALARMSOUND];		// a path to a sound
		
	strcpy(tempDir, [ev alarmSound]);
	tempFile = rindex(tempDir,'/') + 1;
	*(rindex (tempDir,'/')) = '\0';

	#ifdef DEBUG
		fprintf(stderr,"Open directory <%s> with file <%s>\n",
				 tempDir, tempFile);
	#endif
	
	if( [myOpenPanel runModalForDirectory:	tempDir
		file:	tempFile types:  alarmExtensions ] == 0)
		return self;

	#ifdef DEBUG
		fprintf(stderr,"Opening file <%s>\n", [myOpenPanel filename]);
	#endif
			
	[ev setAlarmSound: (char *) [myOpenPanel filename]];
	[alarmSoundForm	setStringValue:	[ev alarmSound]	at: 0];
	return self;
}

- setCassandra: anObject
{
	cassandra = anObject;
	return self;
}


- anniversaryFinished: sender
{
	int radioCol, temp;
	static char buf1[10], buf2[80];

	// The window has changed
	[self setDocEdited:	TRUE];
	
	radioCol = 	[anniversaryRadioMatrix selectedCol];
	temp = 		[anniversaryForm intValueAt:0];
	sprintf(buf1, "%s%c", frequencyChar[radioCol], (temp != 1)?'s':'\0');
	[everyTextField setStringValue: buf1];
	
	if( [anniversaryButton state])
		{
		if( temp == 1)
			sprintf(buf2,"I will remind you every %s.",
				frequencyChar[radioCol]);
		else
			sprintf(buf2,"I will remind you every %d %s.",
				[anniversaryForm intValueAt:0],
				buf1);
		[testAnniversaryTextField setStringValue: buf2];
		}
	else
		[testAnniversaryTextField setStringValue:
			"This is not a reoccuring event."];
	return self;
}

- updateTestDate : (struct tm *) tnow
{
	static char buf[5];
	extern const char *shortWeekDays[7];
	
	[testDateTextField setStringValue:  
		ascMyTime( tnow, NOSEC, [global militaryTime])];
	
	sprintf(buf,"%s", shortWeekDays[tnow->tm_wday]);
	[wdayTextField	setStringValue: buf ];

	return self;
}

// Slight workaround for IB, since IB only likes values 
// that return (id), but I need the (struct tm *) in another 
// part of the program, so all we do is message getDate
// and ignore the return value.
- dateFinished: sender
{
	// The window has changed
	[self setDocEdited:	TRUE];

	[self getDate:sender];
	return self;
}

- (struct tm*) getDate :  (id) sender
{
	static struct tm eventTime;
	int month, year;
	static char buf[100];
	extern const char *shortMonths[12];
		
	eventTime.tm_sec = 0;
	
	// Get the hour form and make sure it is in good shape
	strcpy(buf, [hourForm stringValueAt:0]);
	eventTime.tm_hour = atoi(buf);
	if( ![global militaryTime])	// if we are not using 24hr time
		{
		// then let's check for am/pm suffix
		if( rindex( buf, 'a') || rindex(buf, 'A') || 
			rindex(buf, 'n') || rindex(buf, 'N'))
			[amPmButton setState: AM];		
		else if (rindex(buf, 'p') || rindex(buf,'P') ||
			rindex(buf, 'm') || rindex(buf, 'M'))
			[amPmButton setState: PM];
		}
		
	if( [global militaryTime])
		{
		if( eventTime.tm_hour < 0 || eventTime.tm_hour > 23)
			{
			NXRunAlertPanel( "Time error",
					"I had trouble reading the hour "
					"of the event. "
					"We are using military time. "
					"Please type in a number from "
					"0-23 in the hour form.",
					"OK",NULL,NULL);
			[hourForm selectTextAt:0];
			return NULL;
			}
		}
	else
		{
		if( eventTime.tm_hour < 1 || eventTime.tm_hour > 12)
			{
			NXRunAlertPanel( "Time error",
					"I had trouble reading the hour "
					"of the event. "
					"We are using am/pm time. "
					"Please type in a number from 1-12 "
					"in the hour form.",
					"OK",NULL,NULL);
			[hourForm selectTextAt:0];
			return NULL;
			}

		if(eventTime.tm_hour == 12)
			eventTime.tm_hour = 0;
		eventTime.tm_hour += ( ([amPmButton state] == AM) ? 0 : 12);	
		}
	
	// Get the minutes, check their format
	eventTime.tm_min =  [minuteForm intValueAt:0];
	if( eventTime.tm_min < 0 || eventTime.tm_min > 59)
		{
		NXRunAlertPanel( "Time error",
				"I had trouble reading the minute "
				"of the event. "
				"Please type in a number from 0-59 "
				"in the minute form.",
				"OK",NULL,NULL);
		[minuteForm selectTextAt:0];
		return NULL;
		}
		
	
	if(  (month = monthFromAscii((char*) [monthForm stringValueAt:0]))  == 0)
		{
		[monthForm selectTextAt:0];
		return NULL;	// If the month is in an incorrect format return
		}
	eventTime.tm_mon =  ( month - 1);
	
	
	// Get the year and check its format
	year = [yearForm intValueAt:0];
	if( year < 0 || year > 3000)
		{
		NXRunAlertPanel( "Time error",
				"I had trouble reading the year of the event. "
				"Please type in a number from 0-3000 in the "
				"year form. See the manual or online help "
				"for abbreviations.",
				"OK",NULL,NULL);
		[yearForm selectTextAt:0];
		return NULL;
		}
	if( year < 70)	
		eventTime.tm_year =  100+ year;
	if( year >= 70 && year < 100)
		eventTime.tm_year =  year;
	if( year >= 100)
		eventTime.tm_year = year - 1900;
	
		
	// Get the day, check its form
	eventTime.tm_mday =   [dayForm intValueAt:0];
	if( eventTime.tm_mday < 1 || eventTime.tm_mday > 31)
		{
		sprintf( buf, "I had trouble reading the day of the event. "
				"Please type in a number from 1-31 in the "
				"day form. (Hint: %s. %4d has %d days)", 
			shortMonths[eventTime.tm_mon], 
			eventTime.tm_year + 1900, 
			daysInMonth( eventTime.tm_mon + 1, 
					eventTime.tm_year + 1900));
		NXRunAlertPanel( "Time error",buf,"OK",NULL,NULL);
		[dayForm selectTextAt:0];
		return NULL;
		}
		
	
	fixTmStructure( &eventTime);
	[self updateTestDate: &eventTime];
	[self selectNextText:sender];
	
	return ( &eventTime);
}

- selectNextText:sender
{
	// If the sender is a form class or a subclass of it, then 
	// we want the nextText private variable of the sender, 
	// to use to select the next Form after the user hits return 
	// but NeXT programmers haven't given us a method to access it 
	// so we take it by brute force (I know, I know.... breaking data 
	// abstractions...) 
	// This is out of Cox, "Object Orientated Programming, pp.147 
	

	if( [sender isKindOf:	 [Form class] ] )
		{
		struct idform {@defs(Form);} *p = (struct idform *)sender;
		[p->nextText selectText:self];
		}
	return self;
}



@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.