This is CalendarController.m in view mode; [Download] [Up]
/*
kfc - Kurt's Free Calendar
Copyright 1995 Kurt Werle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#import <objc/List.h>
#import <stdio.h>
#import <appkit/Matrix.h>
#import <misckit/MiscThreeStateButton.h>
#import <misckit/MiscAppDefaults.h>
#import "CalendarController.h"
#import "MonthView.h"
#import "anAppointment.h"
#import "editDayAppointments.h"
#import "appointmentPanelController.h"
#import "appointmentFile.h"
/* Because I don't like the way MiscTimes init (and because the MiscTime object is in transition) I wrote this function to smooth things out. It may well go away in the future (I hope). */
void
fix_initWithCurrentTime_bug (id anID)
{
int aYear = [anID year];
if (aYear < 0)
{
return;
}
if (aYear < 70)
{
[anID setYear:aYear + 2000];
}
if (aYear < 100)
{
[anID setYear:aYear + 1900];
}
}
@implementation CalendarController
- init
{
[super init];
#ifdef DEBUG
printf ("doing init\n");
#endif
currentDay = [[MiscTime alloc] initWithCurrentTime];
fix_initWithCurrentTime_bug (currentDay);
appointmentFileList = [[List alloc] init];
expiredList = [[List alloc] init];
defaultAppointmentFile = nil;
return self;
}
- appDidInit:sender
{
id tempString;
static NXDefaultsVector myDefaults = {
{"hiddenOnLaunch", "NO"},
{"beepForWarningPanels", "NO"},
{"defaultWarning", "0"},
{"sendMailEveryTime", "NO"},
{"justSaveFileNQA", "NO"},
{"lastUsed", ""},
{"defaultFile", ""},
{NULL}
};
#ifdef DEBUG
printf ("appDidInit\n");
#endif
[NXApp registerDefaults:myDefaults];
[calendarWindow setFrameUsingName:"calendar"];
[myMonthView setActiveDay:currentDay];
[calendarWindow makeFirstResponder:myMonthView];
tempString = [[MiscString alloc] init];
[tempString setString:[NXApp defaultValue:"startFiles"]];
/* If we didn't start with a file we load the newuser kfc file. */
if ([appointmentFileList count] == 0)
{
List *fileList;
fileList = [[List alloc] init];
[tempString tokenize:";" into:fileList];
if (![fileList count])
{ /* No files -- give 'em the beginner file. */
[tempString setString:
[(NXBundle *)[NXBundle mainBundle] directory]];
[tempString cat:"/Appointments/newuser.kfc"];
[self app:self openFile:[tempString stringValue] type:"kfc"];
}
else
{ /* Open the files listed in the defaults. */
int i;
for (i = 0; i < [fileList count]; i++)
{
[self app:self openFile:
[[fileList objectAt:i] stringValue] type:"kfc"];
}
}
[fileList freeObjects];
[fileList free];
}
[calendarWindow makeKeyAndOrderFront:self];
if ([NXApp defaultBoolValue:"hiddenOnLaunch"] && [NXApp defaultBoolValue:"NXAutoLaunch"])
{
[calendarWindow performMiniaturize:self];
calendarWindowNeedsMaking = YES;
}
else
{
calendarWindowNeedsMaking = NO;
}
[tempString free];
return self;
}
/* Loads the editor if we never loaded it before. */
- _initializeEditor
{
if (!editController)
{
id tempstring,
editBundle;
tempstring = [[MiscString alloc] initString:
[(NXBundle *)[NXBundle mainBundle] directory]];
[tempstring cat:"/editDayAppointments.bundle"];
editBundle = [[NXBundle alloc] initForDirectory:[tempstring stringValue]];
if (!editBundle)
{
NXRunAlertPanel("KFC.app", "Bogus. Tell Kurt you can't load the editBundle (Send Mail->Bugs).", NULL, NULL, NULL);
}
editController = [[[editBundle principalClass] alloc]
initFromBundle:editBundle withDelegate:self];
if (!editController)
{
NXRunAlertPanel("KFC.app", "Bogus. Tell Kurt you can't load the editController (Send Mail->Bugs).", NULL, NULL, NULL);
}
[tempstring free];
[editBundle free];
}
return self;
}
/* Loads my file panel if we never loaded it before. */
- _filePanel
{
if (!filePanel)
[NXApp loadNibSection:"myFilePanel.nib" owner:self withNames:NO];
return filePanel;
}
/* Tell the user about copying this app. */
- Copying:sender
{
if (!copyPanel)
[NXApp loadNibSection:"copy.nib" owner:self withNames:NO];
[copyPanel makeKeyAndOrderFront:sender];
return self;
}
/* Tell the user about this app. */
- InfoBahn:sender
{
if (!infoPanel)
[NXApp loadNibSection:"Info.nib" owner:self withNames:NO];
[infoPanel makeKeyAndOrderFront:sender];
return self;
}
- free
{
if (currentDay)
{
[currentDay free];
}
if (appointmentFileList)
{
[appointmentFileList free];
}
if (expiredList)
{
[expiredList free];
}
return [super free];
}
/* Make the window if we need to. */
- appDidUnhide:sender
{
#ifdef DEBUG
printf ("doing appDidUnhide\n");
#endif
if (calendarWindowNeedsMaking)
{
[calendarWindow makeKeyAndOrderFront:self];
calendarWindowNeedsMaking = NO;
}
return self;
}
/* Should we give the user the option to cancel this exit? Not if the power is going off... */
- terminateWithCancel:(BOOL)withCancel
{
MiscTime *exitTime;
MiscString *exitTimeString;
[self checkEdited];
if ([calendarWindow isDocEdited])
{
int shouldExit;
if (withCancel)
{
shouldExit = NXRunAlertPanel ("Quit", "There are edited files.", "Save All", "Quit Anyway", "Cancel");
}
else
{
shouldExit = NXRunAlertPanel ("Quit", "There are edited files.", "Save All", "Quit Anyway", NULL);
}
switch (shouldExit)
{
case NX_ALERTDEFAULT:
{
[self saveAll:self];
break;
}
case NX_ALERTALTERNATE:
{
break;
}
case NX_ALERTOTHER:
default:
{
return nil;
}
}
}
exitTime = [[MiscTime alloc] initWithCurrentTime];
fix_initWithCurrentTime_bug (exitTime);
[calendarWindow saveFrameUsingName:"calendar"];
exitTimeString = [[MiscString alloc] init];
[exitTimeString setFromFormat:"%02d:%02d:%02d:%02d:%02d",[exitTime year],[exitTime month],[exitTime day],[exitTime hour],[exitTime minute]];
[NXApp setDefault:"lastUsed" to:[exitTimeString stringValue]];
[exitTime free];
return self;
}
/* Handle some app events. */
- app:sender powerOffIn:(int)ms andSave:(int)aFlag
{
return[self terminateWithCancel:NO];
}
- appWillTerminate:sender
{
return[self terminateWithCancel:YES];
}
- load_Warnings_For_File:theFile
{
int i;
List *appointmentList;
#ifdef DEBUG
printf ("load_Warnings_For_File: theFile\n");
#endif
/* Go through the whole list of appointments. */
appointmentList = [theFile appointmentList];
if (appointmentList == nil)
{
return self;
}
for (i = 0; i < [appointmentList count]; i++)
{
[self queue_Warning:[appointmentList objectAt:i]];
}
return self;
}
/* Do something useful with an appointment warning depending on when it becomes active - never (the past or distant future), now, or later. */
- queue_Warning:theWarning
{
MiscTime *nowTime;
/*
#ifdef DEBUG
printf ("queue_Warning: %s\n",[[theWarning appointmentText] stringValue]);
#endif
*/
nowTime = [[MiscTime alloc] initWithCurrentTime];
fix_initWithCurrentTime_bug (nowTime);
/* Is this warning applicable today? */
switch ([theWarning warnForDay:nowTime])
{
case -1:
{
/* Does not apply to today. */
break;
}
case 0:
{
[self execute_Warning:theWarning];
break;
}
default:
{
/* Must be run in the near future. */
int delaySeconds = [theWarning warnForDay:nowTime];
if (delaySeconds > 0)
{
[self perform:@selector (execute_Warning:)
with :theWarning afterDelay:delaySeconds * 1000
cancelPrevious:NO];
}
break;
}
}
[nowTime free];
return self;
}
/* Do whatever is required by this timely warning. */
- execute_Warning:theWarning
{
MiscTime *lastCheck = nil;
/* Are we supposed to do repeat mailings? */
if (![NXApp defaultBoolValue:"sendMailEveryTime"])
{
MiscString *tempString;
List *timeList;
timeList = [[List alloc] init];
tempString = [[MiscString alloc] initString:
[NXApp defaultValue:"lastUsed"]];
[tempString tokenize:":" into:timeList];
if ([timeList count] == 5)
{
lastCheck = [[MiscTime alloc] initWithCurrentTime];
fix_initWithCurrentTime_bug (lastCheck);
[lastCheck setYear:[[timeList objectAt:0] intValue]];
[lastCheck setMonth:[[timeList objectAt:1] intValue]];
[lastCheck setDay:[[timeList objectAt:2] intValue]];
[lastCheck setHour:[[timeList objectAt:3] intValue]];
[lastCheck setMinute:[[timeList objectAt:4] intValue]];
}
[tempString free];
[[timeList freeObjects] free];
}
if ([theWarning wantsPanel])
{
if ([NXApp defaultBoolValue:"beepForWarningPanels"])
{
NXBeep ();
}
[[appointmentPanelController alloc] initWithController:self
withAppointment:theWarning];
}
if ([theWarning wantsMailing])
{
if ((lastCheck == nil) || ([theWarning warnForDay:lastCheck] != 0))
[self mailAppointment:theWarning];
}
if ([theWarning trepeat])
{
[self perform:@selector (queue_Warning:)
with :theWarning afterDelay:[theWarning trepeat] * 60 * 1000
cancelPrevious:NO];
}
if (lastCheck != nil)
{
[lastCheck free];
}
return self;
}
- mailAppointment:theAppointment
{
FILE *a_mail_file;
MiscString *tempString;
tempString = [[MiscString alloc] initString:"/usr/ucb/mail -s \""];
[tempString cat:[theAppointment appointmentTextFirstLine]];
[tempString cat:"\" "];
[tempString cat:getlogin ()];
a_mail_file = popen ([tempString stringValue], "wt");
if (a_mail_file)
{
fprintf (a_mail_file,
"This is an appointment warning from kfc.app.\n");
fprintf (a_mail_file, "%s\n",
[[theAppointment appointmentText] stringValue]);
if ([theAppointment weekDay] != -1)
{
fprintf (a_mail_file, [theAppointment format:" The first %A following "] + 1);
}
if ([theAppointment month] != -1)
{
fprintf (a_mail_file, "%d ",[theAppointment month] + 1);
}
else
{
fprintf (a_mail_file, "of every month ");
}
if ([theAppointment day] != -1)
{
fprintf (a_mail_file, "%d ",[theAppointment day] + 1);
}
else
{
fprintf (a_mail_file, "Every day ");
}
if ([theAppointment year] != -1)
{
fprintf (a_mail_file, "%d\n",[theAppointment year] + 1);
}
else
{
fprintf (a_mail_file, "of every year.\n");
}
if ([theAppointment hour] != -1)
{
fprintf (a_mail_file, " AT %2d:%2d\n",[theAppointment hour],[theAppointment minute]);
}
fprintf (a_mail_file, "%c\n\n\n", EOF);
pclose (a_mail_file);
}
else
{
NXRunAlertPanel ("Sending mail for this appointment failed!",
[[theAppointment appointmentText] stringValue],
NULL, NULL, NULL);
}
return self;
}
/* Edit a list of expired appointments. */
- expired_Appointments:sender
{
List *appointmentList;
int i,
j;
MiscTime *todaysDate;
[expiredList init];
todaysDate = [[MiscTime alloc] initWithCurrentTime];
fix_initWithCurrentTime_bug (todaysDate);
[self _initializeEditor];
for (j = 0; j < [appointmentFileList count]; j++)
{
appointmentList = [[appointmentFileList objectAt:j] appointmentList];
if (appointmentList == nil)
{
continue;
}
for (i = 0; i < [appointmentList count]; i++)
{
if ([[appointmentList objectAt:i]
isExpiredForDay:[todaysDate day]
Month:[todaysDate month]
Year:[todaysDate year]])
{
[expiredList addObject:[appointmentList objectAt:i]];
}
} /* One file's List */
} /* All the open files */
[editController editForDay:todaysDate withList:expiredList];
[editController beginEdit];
return self;
}
/* Make the currently pointed at day today. */
- today:sender
{
[currentDay initWithCurrentTime];
fix_initWithCurrentTime_bug (currentDay);
[self update_MonthView_With_CurrentDay];
return self;
}
/* Go to last month. */
- Last_Month:sender
{
[currentDay subtractMonths:1];
[currentDay setDay:MIN ([currentDay day],
[MiscTime daysInMonth:[currentDay month]
ofYear:[currentDay year]] - 1)];
[self update_MonthView_With_CurrentDay];
return self;
}
/* Go to next month. */
- Next_Month:sender
{
if (sender == lastMonthView)
{
return[self Last_Month:sender];
}
[currentDay addMonths:1];
[currentDay setDay:MIN ([currentDay day],
[MiscTime daysInMonth:[currentDay month]
ofYear:[currentDay year]] - 1)];
[self update_MonthView_With_CurrentDay];
return self;
}
- (BOOL)add_Appointment:newAppointment
{
if ([appointmentFileList count] == 0)
{
NXRunAlertPanel ("New Appointment.",
"You can not create a new appointment without a file open!",
NULL, NULL, NULL);
return NO;
}
/*
There's a way to do the logic of this if statement without so many
parens, but I'm feeling lazy and the compiler optimizes, right?
*/
/* Should we prompt the user to see which file to save the new appointment to? */
if (!((([NXApp defaultBoolValue:"justSaveFileNQA"]) &&
([appointmentFileList indexOf:defaultAppointmentFile] != NX_NOT_IN_LIST)) ||
([appointmentFileList count] == 1)))
{
id theFilePanel;
id tempView;
id aCell;
int defaultFileNumber,
i;
NXRect myViewRect;
theFilePanel = [self _filePanel];
[filePanelBox setTitle:"New Appointment For Which File"];
[filePanelBox display];
[filePanelActionButton setTarget:self];
[filePanelActionButton setAction:@selector (newForFile:)];
[filePanelActionButton setTitle:"Add"];
[filePanelCancelButton setTarget:self];
[filePanelCancelButton setAction:@selector (cancelNew:)];
[filePanelScrollView setVertScrollerRequired:YES];
[filePanelScrollView setBorderType:NX_LINE];
[filePanelScrollView getDocVisibleRect:&myViewRect];
tempView = [filePanelScrollView setDocView:[[Matrix alloc] initFrame:&myViewRect
mode:NX_RADIOMODE
cellClass:[ButtonCell class]
numRows:0
numCols:1]];
if (tempView)
[tempView free];
tempView = [filePanelScrollView docView];
[tempView setEmptySelectionEnabled:YES];
{
NXSize cellSize;
[tempView getCellSize:&cellSize];
cellSize.width = myViewRect.size.width;
[tempView setCellSize:&cellSize];
}
aCell = [[ButtonCell alloc] init];
[aCell setType:NX_ONOFF];
[aCell setBordered:NO];
[aCell setAlignment:NX_LEFTALIGNED];
aCell = [tempView setPrototype:aCell];
if (aCell)
[aCell free];
[self loadMatrix:tempView withFiles:LOADED_FILES];
[tempView setTarget:self];
[tempView setDoubleAction:@selector (newForFile:)];
[tempView clearSelectedCell];
if ((defaultFileNumber = [appointmentFileList indexOf:defaultAppointmentFile]) != NX_NOT_IN_LIST)
{
[tempView selectCellAt:defaultFileNumber :0];
[tempView scrollCellToVisible:defaultFileNumber :0];
}
for (i = 0; i < [appointmentFileList count]; i++)
{
if (MISCFILE_SUCCESS != (int)[[[appointmentFileList objectAt:i] myFile] access:MISCFILE_WRITE])
{
[[tempView cellAt:i :0] setEnabled:NO];
}
}
[tempView update];
{
int listNumber = [NXApp runModalFor:theFilePanel];
if (listNumber != -1)
{
[[appointmentFileList objectAt:listNumber] addAppointment:newAppointment];
return YES;
[self checkEdited];
}
else
{
return NO;
}
}
}
if ([appointmentFileList count] == 1)
{
if (MISCFILE_SUCCESS != (int)[[[appointmentFileList objectAt:0] myFile] access:MISCFILE_WRITE])
{
NXRunAlertPanel ("New Appointment",
"You can not write to the Appointment File you have open. Open another.",
NULL, NULL, NULL);
return NO;
}
[[appointmentFileList objectAt:0] addAppointment:newAppointment];
}
else
{
if (MISCFILE_SUCCESS != (int)[[[appointmentFileList objectAt:0] myFile] access:MISCFILE_WRITE])
{
NXRunAlertPanel ("New Appointment",
"You can not write to the Default Appointment File. Select another.",
NULL, NULL, NULL);
return NO;
}
[defaultAppointmentFile addAppointment:newAppointment];
}
return YES;
}
- remove_Appointment:oldAppointment
{
/*
Quick hack that will do the job. This way appointments do not have to
know what file they belong to (which is how it should be).
*/
/* Make all the open appointment files try to remove the deleted appointments. */
[appointmentFileList makeObjectsPerform:
@selector (removeAppointment:) with :oldAppointment];
[self update_MonthView_With_CurrentDay];
[self checkEdited];
return self;
}
/* Don't add that new appointment. */
- cancelNew:sender
{
[NXApp stopModal:-1];
[filePanel close];
return self;
}
/* Add that new appointment. */
- newForFile:sender
{
if ([[filePanelScrollView docView] selectedRow] != NX_NOT_IN_LIST)
{
int i;
id theFileToSave;
theFileToSave = [allFileList objectAt:[[filePanelScrollView docView] selectedRow]];
for (i = 0; i < [appointmentFileList count]; i++)
{
if ([[appointmentFileList objectAt:i] myFile] == theFileToSave)
{
[NXApp stopModal:i];
[filePanel close];
return self;
}
}
}
[NXApp stopModal:-1];
[filePanel close];
return self;
}
/* Check to see if any of the appointment files need saving. */
- checkEdited
{
int i;
BOOL haveEdited = NO;
for (i = 0; i < [appointmentFileList count]; i++)
{
haveEdited |= [[appointmentFileList objectAt:i] hasBeenEdited];
}
[calendarWindow setDocEdited:haveEdited];
return self;
}
/* Display the mini-month views. */
- displayAppointments_For_Next_and_Last
{
int i,
j;
List *displayTimes;
id nextMonth,
lastMonth;
nextMonth = [currentDay copy];
lastMonth = [currentDay copy];
[nextMonth addMonths:1];
[lastMonth subtractMonths:1];
#ifdef DEBUG
printf ("doing displayAppointments\n");
#endif
/* Go through the whole list of appointments. */
for (j = 0; j < [appointmentFileList count]; j++)
{
List *appointmentList;
appointmentList = [[appointmentFileList objectAt:j] appointmentList];
if (appointmentList == nil)
{
continue;
}
for (i = 0; i < [appointmentList count]; i++)
{
/*
Get a list of appointments for the curent item for next month.
*/
displayTimes = [[appointmentList objectAt:i]
displayTimesForMonth:[nextMonth month]
andYear:[nextMonth year]];
/* Tell the current month to display the list for the curent item. */
[nextMonthView displayAppointment:[appointmentList objectAt:i]
atTimes:displayTimes];
/* Free the list's contents and the list itself. */
[[displayTimes freeObjects] free];
/*
Get a list of appointments for the curent item for last month.
*/
displayTimes = [[appointmentList objectAt:i]
displayTimesForMonth:[lastMonth month]
andYear:[lastMonth year]];
/* Tell the current month to display the list for the curent item. */
[lastMonthView displayAppointment:[appointmentList objectAt:i]
atTimes:displayTimes];
/* Free the list's contents and the list itself. */
[[displayTimes freeObjects] free];
}
}
[nextMonth free];
[lastMonth free];
return self;
}
- displayAppointments
{
int i,
j;
List *displayTimes;
[self displayAppointments_For_Next_and_Last];
#ifdef DEBUG
printf ("doing displayAppointments\n");
#endif
/* Go through the whole list of appointments. */
for (j = 0; j < [appointmentFileList count]; j++)
{
List *appointmentList;
appointmentList = [[appointmentFileList objectAt:j] appointmentList];
if (appointmentList == nil)
{
continue;
}
for (i = 0; i < [appointmentList count]; i++)
{
/*
Get a list of appointments for the curent item for the current
month.
*/
displayTimes = [[appointmentList objectAt:i]
displayTimesForMonth:[currentDay month]
andYear:[currentDay year]];
/* Tell the current month to display the list for the curent item. */
[myMonthView displayAppointment:[appointmentList objectAt:i]
atTimes:displayTimes];
/* Free the list's contents and the list itself. */
[[displayTimes freeObjects] free];
}
}
return self;
}
- update_MonthView_With_CurrentDay
{
char TempString[20];
char MonthText[12][20] = {"January", "February", "March",
"April", "May", "June", "July",
"August", "September", "October",
"November", "December"};
#ifdef DEBUG
printf ("doing update_MonthView_With_CurrentDay\n");
#endif
[calendarWindow disableFlushWindow];
/* Set up the Next and Last month mini-views. */
{
id nextMonth,
lastMonth;
nextMonth = [currentDay copy];
lastMonth = [currentDay copy];
[[nextMonth setDay:0] addMonths:1];
[[lastMonth setDay:0] subtractMonths:1];
[myMonthView initMonthWithFirstDay:currentDay];
[nextMonthView initMonthWithFirstDay:nextMonth];
[lastMonthView initMonthWithFirstDay:lastMonth];
[nextMonth free];
[lastMonth free];
}
sprintf (TempString, "%d",[currentDay month] + 1);
[monthText setStringValue:MonthText[[currentDay month]]];
sprintf (TempString, "%d",[currentDay year]);
[[[yearBox superview] superview] sizeBy:1 :0];
[yearBox setTitle:TempString];
[yearBox sizeTo:0 :0];
[yearBox sizeToFit];
[yearBox display];
[self displayAppointments];
[myMonthView display];
[nextMonthView display];
[lastMonthView display];
[[calendarWindow reenableFlushWindow] flushWindow];
return self;
}
/* Load a matrix with the appropriate list of files depending on the circumstances. */
- loadMatrix:theMatrix withFiles:(int)kindOfFiles
{
int i;
id aCell;
MiscFile *userDirectory,
*localLibraryDirectory,
*kfcDirectory;
appointmentFile *oneAppointmentFile;
MiscString *tempString = [[MiscString alloc] init];
if (allFileList)
{
[allFileList free];
}
allFileList = [[List alloc] init];
/*
Now allFileList has a list of all the files. Let's get rid of the ones
we don't want for this matrix.
*/
switch (kindOfFiles)
{
case EDITED_FILES:
case LOADED_FILES:
{
for (i = 0; i < [appointmentFileList count]; i++)
{
oneAppointmentFile = [appointmentFileList objectAt:i];
if (([oneAppointmentFile hasBeenEdited] &&
(kindOfFiles == EDITED_FILES)) ||
(kindOfFiles == LOADED_FILES))
[allFileList addObject:[oneAppointmentFile myFile]];
}
break;
}
case YET_TO_LOAD_FILES:
case ALL_FILES:
default:
{
for (i = 0; i < [appointmentFileList count]; i++)
{
oneAppointmentFile = [appointmentFileList objectAt:i];
if (([oneAppointmentFile hasBeenEdited] &&
(kindOfFiles == EDITED_FILES)) ||
(kindOfFiles == LOADED_FILES) ||
(kindOfFiles == ALL_FILES))
[allFileList addObject:[oneAppointmentFile myFile]];
}
/* Find any files in the local Library. */
localLibraryDirectory = [[MiscFile alloc] initWithPath:
"/LocalLibrary/kfc"];
[self addFilesFrom:localLibraryDirectory toList:allFileList];
/* Find any files in the kfc Appointments directory. */
[tempString setString:[(NXBundle *)[NXBundle mainBundle] directory]];
[tempString cat:"/Appointments"];
kfcDirectory = [[MiscFile alloc] initWithPath:
[tempString stringValue]];
[self addFilesFrom:kfcDirectory toList:allFileList];
/* Find any files in the user's directory. */
[tempString setString:NXHomeDirectory ()];
[tempString cat:"/Library/kfc"];
userDirectory = [[MiscFile alloc] initWithPath:
[tempString stringValue]];
[self addFilesFrom:userDirectory toList:allFileList];
if (kindOfFiles == YET_TO_LOAD_FILES)
{
/* What files do we have loaded. */
for (i = 0; i < [appointmentFileList count]; i++)
{
oneAppointmentFile = [[appointmentFileList objectAt:i]
myFile];
/* So we only list this object once. */
[allFileList removeObject:oneAppointmentFile];
}
}
break;
}
}
for (i = 0; i < [allFileList count]; i++)
{
oneAppointmentFile = [allFileList objectAt:i];
[theMatrix addRow];
aCell = [theMatrix cellAt:i :0];
[aCell setTitle:[[allFileList objectAt:i] filename]];
if ([appointmentFileList indexOf:oneAppointmentFile] != NX_NOT_IN_LIST)
{
[aCell setIntValue:1];
}
}
[theMatrix sizeToCells];
[tempString free];
return self;
}
/* Get a list of kfc files from a given directory... */
- addFilesFrom:(MiscFile *) appointmentDirectory toList:(List *) theList
{
id myList = theList;
BOOL returnList = NO;
if (theList == nil)
{
returnList = YES;
myList = [[List alloc] init];
}
if (appointmentDirectory != nil)
{
List *listToAdd = [appointmentDirectory children];
int i;
for (i = 0; i < [listToAdd count]; i++)
{
[myList addObjectIfAbsent:[listToAdd objectAt:i]];
}
}
if (returnList)
{
return myList;
}
return self;
}
/* Notify the correct file that it has a modified appointment. */
- notifyChange:changedAppointment
{
int i;
for (i = 0; i < [appointmentFileList count]; i++)
{
if ([[[appointmentFileList objectAt:i] appointmentList]
indexOf:changedAppointment] != NX_NOT_IN_LIST)
{
[[appointmentFileList objectAt:i]
appointmentChanged:changedAppointment];
[self checkEdited];
return self;
}
}
return self;
}
- save_Appointments:sender
{
int i;
for (i = 0; i < [appointmentFileList count]; i++)
{
[[appointmentFileList objectAt:i] saveFile:self];
}
[self checkEdited];
return self;
}
- load_Appointments:sender
{
int i;
#ifdef DEBUG
printf ("load_Appointments:\n");
#endif
for (i = 0; i < [appointmentFileList count]; i++)
{
[[appointmentFileList objectAt:i] loadFile:self];
}
return self;
}
@end
/* Delegate methods */
@implementation CalendarController (Delegate_Methods)
- windowDidResize:sender
{
[self update_MonthView_With_CurrentDay];
return self;
}
- editDaysAppointments:theDay
{
[self _initializeEditor];
[editController editForDay:[theDay myDate] withList:[theDay appointmentList]];
[editController beginEdit];
return self;
}
- edit_Appointment:theAppointment;
{
[self _initializeEditor];
[editController editAppointment:theAppointment];
[editController beginEdit];
return self;
}
- addNote:(char *)TheNote forDay:(int)monthDay
{
return self;
}
- doneEditing
{
[self update_MonthView_With_CurrentDay];
return self;
}
@end
@implementation CalendarController (Application_Delegate_Methods)
- (BOOL)appAcceptsAnotherFile:sender
{
return YES;
}
- (int)app:sender openFile:(const char *)filename type:(const char *)aType
{
MiscFile *newFile;
appointmentFile *newappointmentFile;
int i;
#ifdef DEBUG
printf ("openFile:\n");
#endif
newFile = [[MiscFile alloc] initWithPath:filename];
if (!newFile)
{
return NO;
}
for (i = 0; i < [appointmentFileList count]; i++)
{
if (newFile == [[appointmentFileList objectAt:i] myFile])
{
return NO;
}
}
newappointmentFile = [[appointmentFile alloc] initWithPath:filename
WithController:self];
[appointmentFileList addObject:newappointmentFile];
if ([NXApp defaultValue:"defaultFile"] && strstr ([[newappointmentFile myFile] fullPath],[NXApp defaultValue:"defaultFile"]))
{
defaultAppointmentFile = newappointmentFile;
}
[newappointmentFile loadFile:self];
[self load_Warnings_For_File:newappointmentFile];
[self update_MonthView_With_CurrentDay];
return YES;
}
@end
@implementation CalendarController (File_Operations)
- open:sender
{
id theFilePanel;
id tempView;
id aCell;
NXRect myViewRect;
theFilePanel = [self _filePanel];
[filePanelActionButton setTarget:self];
[filePanelActionButton setAction:@selector (openTheFile:)];
[filePanelActionButton setTitle:"Open"];
[filePanelCancelButton setTarget:self];
[filePanelCancelButton setAction:@selector (cancelFile:)];
[filePanelBox setTitle:"Double Click to Open"];
[filePanelBox display];
[filePanelScrollView setVertScrollerRequired:YES];
[filePanelScrollView setBorderType:NX_LINE];
[filePanelScrollView getDocVisibleRect:&myViewRect];
tempView = [filePanelScrollView setDocView:[[Matrix alloc] initFrame:&myViewRect
mode:NX_RADIOMODE
cellClass:[ButtonCell class]
numRows:0
numCols:1]];
if (tempView)
[tempView free];
tempView = [filePanelScrollView docView];
[tempView setEmptySelectionEnabled:YES];
{
NXSize cellSize;
[tempView getCellSize:&cellSize];
cellSize.width = myViewRect.size.width;
[tempView setCellSize:&cellSize];
}
aCell = [[ButtonCell alloc] init];
/* [aCell setTarget:self]; */
[aCell setType:NX_ONOFF];
[aCell setBordered:NO];
[aCell setAlignment:NX_LEFTALIGNED];
aCell = [tempView setPrototype:aCell];
if (aCell)
[aCell free];
[self loadMatrix:tempView withFiles:YET_TO_LOAD_FILES];
[tempView setTarget:self];
[tempView setDoubleAction:@selector (openTheFile:)];
[tempView clearSelectedCell];
[tempView update];
[NXApp runModalFor:theFilePanel];
return self;
}
- cancelFile:sender
{
[filePanel close];
[NXApp stopModal];
return self;
}
- openTheFile:sender
{
[NXApp stopModal];
[filePanel close];
if ([[filePanelScrollView docView] selectedRow] == -1)
{
id myOpenPanel;
int i;
myOpenPanel = [OpenPanel new];
i = [myOpenPanel runModalForTypes:("kfc", NULL)];
if (i == NX_OKTAG)
{
[self app:self openFile:[myOpenPanel filename] type:"kfc"];
}
}
else
{
[self app:self openFile:[[allFileList objectAt:
[[filePanelScrollView docView] selectedRow]]
fullPath] type:"kfc"];
}
return self;
}
- new:sender
{
id mySavePanel;
int saveStatus;
mySavePanel = [SavePanel new];
saveStatus = [mySavePanel runModal];
if (saveStatus == NX_OKTAG)
{
FILE *newfile;
MiscString *newFileName;
newFileName = [[MiscString alloc] initString:[mySavePanel filename]];
if (![newFileName grep:".kfc$" caseSensitive:YES])
{
[newFileName cat:".kfc"];
}
newfile = fopen ([newFileName stringValue], "wt");
if (!newfile)
{
NXRunAlertPanel ("New File Failed",
"Could not open the new file %s",
NULL, NULL, NULL,[mySavePanel filename]);
}
else
{
fclose (newfile);
}
[self app:self openFile:[newFileName stringValue] type:"kfc"];
[newFileName free];
}
return self;
}
- save:sender
{
id theFilePanel;
id tempView;
id aCell;
NXRect myViewRect;
theFilePanel = [self _filePanel];
[filePanelBox setTitle:"Double Click to Save"];
[filePanelBox display];
[filePanelActionButton setTarget:self];
[filePanelActionButton setAction:@selector (saveTheFile:)];
[filePanelActionButton setTitle:"Save"];
[filePanelCancelButton setTarget:self];
[filePanelCancelButton setAction:@selector (cancelFile:)];
[filePanelScrollView setVertScrollerRequired:YES];
[filePanelScrollView setBorderType:NX_LINE];
[filePanelScrollView getDocVisibleRect:&myViewRect];
tempView = [filePanelScrollView setDocView:[[Matrix alloc] initFrame:&myViewRect
mode:NX_RADIOMODE
cellClass:[ButtonCell class]
numRows:0
numCols:1]];
if (tempView)
[tempView free];
tempView = [filePanelScrollView docView];
[tempView setEmptySelectionEnabled:YES];
{
NXSize cellSize;
[tempView getCellSize:&cellSize];
cellSize.width = myViewRect.size.width;
[tempView setCellSize:&cellSize];
}
aCell = [[ButtonCell alloc] init];
/* [aCell setTarget:self]; */
[aCell setType:NX_ONOFF];
[aCell setBordered:NO];
[aCell setAlignment:NX_LEFTALIGNED];
aCell = [tempView setPrototype:aCell];
if (aCell)
[aCell free];
[self loadMatrix:tempView withFiles:EDITED_FILES];
[tempView setTarget:self];
[tempView setDoubleAction:@selector (saveTheFile:)];
[tempView clearSelectedCell];
[tempView update];
[NXApp runModalFor:theFilePanel];
return self;
}
- saveTheFile:sender
{
[NXApp stopModal];
[filePanel close];
if ([[filePanelScrollView docView] selectedRow] != NX_NOT_IN_LIST)
{
int i;
id theFileToSave;
theFileToSave = [allFileList objectAt:[[filePanelScrollView docView] selectedRow]];
for (i = 0; i < [appointmentFileList count]; i++)
{
if ([[appointmentFileList objectAt:i] myFile] == theFileToSave)
{
[[appointmentFileList objectAt:i] saveFile:self];
[self checkEdited];
return self;
}
}
}
[self checkEdited];
return self;
}
- saveAll:sender
{
int i;
for (i = 0; i < [appointmentFileList count]; i++)
{
if ([[appointmentFileList objectAt:i] hasBeenEdited])
{
[[appointmentFileList objectAt:i] saveFile:self];
}
}
[self checkEdited];
return self;
}
- close:sender
{
id theFilePanel;
id tempView;
id aCell;
NXRect myViewRect;
theFilePanel = [self _filePanel];
[filePanelBox setTitle:"Double Click to Close"];
[filePanelBox display];
[filePanelActionButton setTarget:self];
[filePanelActionButton setAction:@selector (closeTheFile:)];
[filePanelActionButton setTitle:"Close"];
[filePanelCancelButton setTarget:self];
[filePanelCancelButton setAction:@selector (cancelFile:)];
[filePanelScrollView setVertScrollerRequired:YES];
[filePanelScrollView setBorderType:NX_LINE];
[filePanelScrollView getDocVisibleRect:&myViewRect];
tempView = [filePanelScrollView setDocView:[[Matrix alloc] initFrame:&myViewRect
mode:NX_RADIOMODE
cellClass:[ButtonCell class]
numRows:0
numCols:1]];
if (tempView)
[tempView free];
tempView = [filePanelScrollView docView];
[tempView setEmptySelectionEnabled:YES];
{
NXSize cellSize;
[tempView getCellSize:&cellSize];
cellSize.width = myViewRect.size.width;
[tempView setCellSize:&cellSize];
}
aCell = [[ButtonCell alloc] init];
[aCell setType:NX_ONOFF];
[aCell setBordered:NO];
[aCell setAlignment:NX_LEFTALIGNED];
aCell = [tempView setPrototype:aCell];
if (aCell)
[aCell free];
[self loadMatrix:tempView withFiles:LOADED_FILES];
[tempView setTarget:self];
[tempView setDoubleAction:@selector (closeTheFile:)];
[tempView clearSelectedCell];
[tempView update];
[NXApp runModalFor:theFilePanel];
return self;
}
- closeTheFile:sender
{
[NXApp stopModal];
[filePanel close];
if ([[filePanelScrollView docView] selectedRow] != NX_NOT_IN_LIST)
{
int i;
id theFileToClose;
theFileToClose = [allFileList objectAt:[[filePanelScrollView docView] selectedRow]];
for (i = 0; i < [appointmentFileList count]; i++)
{
if ([[appointmentFileList objectAt:i] myFile] == theFileToClose)
{
id appointmentFileToClose;
appointmentFileToClose = [appointmentFileList objectAt:i];
if ([appointmentFileToClose hasBeenEdited])
{
int cancelValue;
cancelValue = NXRunAlertPanel ("Close", "Save changes to %s", "Cancel", "Don't Save", "Save",[[appointmentFileToClose myFile] filename]);
switch (cancelValue)
{
case NX_ALERTDEFAULT:
{
[appointmentFileToClose saveFile:self];
break;
}
case NX_ALERTOTHER:
{
return self;
}
case NX_ALERTALTERNATE:
default:
{
break;
}
} /* switch */
} /* hasBeenEdited */
[appointmentFileList removeObject:appointmentFileToClose];
[appointmentFileToClose free];
[self update_MonthView_With_CurrentDay];
[self checkEdited];
return self;
} /* ([[appointmentFileList objectAt:i] myFile] ==
theFileToClose) */
} /* (i = 0; i < [appointmentFileList count]; i++) */
} /* ([[filePanelScrollView docView] selectedRow] != NX_NOT_IN_LIST) */
return self;
} /* closeTheFile:sender */
@end /* (File_Operations) */
@implementation CalendarController (Preferences_Methods)
- preferences:sender;
{
int i;
id aCell;
if (!prefPanel)
{
NXSize myCellSize,
myFrameSize;
[NXApp loadNibSection:"Preferences.nib" owner:self withNames:NO];
[prefFileBrowser setDocView:prefFileSwitcheMatrix];
[prefFileBrowser setVertScrollerRequired:YES];
[prefFileBrowser setBorderType:NX_BEZEL];
while ([prefFileSwitcheMatrix cellCount])
{
[prefFileSwitcheMatrix removeRowAt:0 andFree:YES];
}
[prefFileSwitcheMatrix getCellSize:&myCellSize];
[prefFileBrowser getContentSize:&myFrameSize];
myCellSize.width = myFrameSize.width;
[prefFileSwitcheMatrix setCellSize:&myCellSize];
aCell = [prefFileSwitcheMatrix prototype];
[aCell setTarget:self];
[aCell setAction:@selector (fileState:)];
aCell = [prefFileSwitcheMatrix setPrototype:aCell];
}
/* Clear the Matrix for another go 'round. */
while ([prefFileSwitcheMatrix cellCount])
{
[prefFileSwitcheMatrix removeRowAt:0 andFree:YES];
}
[self loadMatrix:prefFileSwitcheMatrix withFiles:ALL_FILES];
for (i = 0; i < [prefFileSwitcheMatrix cellCount]; i++)
{
id oneAppointmentFile = [allFileList objectAt:i];
aCell = [prefFileSwitcheMatrix cellAt:i :0];
[aCell setAltTitle:[aCell title]];
[aCell setThirdTitle:[aCell title]];
if ([NXApp defaultValue:"startFiles"] && strstr ([NXApp defaultValue:"startFiles"],[oneAppointmentFile fullPath]))
{
[aCell setIntValue:1];
if ([NXApp defaultValue:"defaultFile"] && strstr ([NXApp defaultValue:"defaultFile"],[oneAppointmentFile fullPath]))
{
[aCell setIntValue:2];
}
}
}
[prefFileSwitcheMatrix update];
[[prefSwitches cellAt:0 :0] setIntValue:
[NXApp defaultBoolValue:"hiddenOnLaunch"]];
[[prefSwitches cellAt:1 :0] setIntValue:
[NXApp defaultBoolValue:"beepForWarningPanels"]];
[[prefSwitches cellAt:2 :0] setIntValue:
[NXApp defaultBoolValue:"sendMailEveryTime"]];
[[prefSwitches cellAt:3 :0] setIntValue:
[NXApp defaultBoolValue:"justSaveFileNQA"]];
[[[defaultWarningPopupButton target] itemList]
selectCellWithTag:[NXApp defaultIntValue:"defaultWarning"]];
[defaultWarningPopupButton setTitle:
[[[[defaultWarningPopupButton target] itemList] selectedCell] title]];
[NXApp runModalFor:prefPanel];
return self;
}
- cancelPreferences:sender
{
/*
We can not free the contents of allFileList as we may be using them in
other lists.
*/
[allFileList free];
allFileList = nil;
[[sender window] close];
[NXApp stopModal];
return self;
}
- savePreferences:sender
{
int i;
MiscString *listOfFiles;
listOfFiles = [[MiscString alloc] initString:""];
[NXApp setDefault:"hiddenOnLaunch" toBool:[[prefSwitches cellAt:0 :0] intValue]];
[NXApp setDefault:"beepForWarningPanels" toBool:[[prefSwitches cellAt:1 :0] intValue]];
[NXApp setDefault:"sendMailEveryTime" toBool:[[prefSwitches cellAt:2 :0] intValue]];
[NXApp setDefault:"justSaveFileNQA" toBool:[[prefSwitches cellAt:3 :0] intValue]];
[NXApp setDefault:"defaultWarning" toInt:
[[[[defaultWarningPopupButton target] itemList] selectedCell] tag]];
/* Blank it if they don't want a default file. */
[NXApp setDefault:"defaultFile" to:""];
for (i = 0; i < [prefFileSwitcheMatrix cellCount]; i++)
{
if ([[prefFileSwitcheMatrix cellAt:i :0] intValue])
{
[listOfFiles cat:[[allFileList objectAt:i] fullPath]];
[listOfFiles cat:";"];
}
if ([[prefFileSwitcheMatrix cellAt:i :0] intValue] == 2)
{
[NXApp setDefault:"defaultFile" to:[[allFileList objectAt:i] fullPath]];
}
}
[NXApp setDefault:"startFiles" to:[listOfFiles stringValue]];
/* Call cancel to free everything... */
[self cancelPreferences:sender];
[listOfFiles free];
return self;
}
- fileState:sender
{
int i;
if ([[prefFileSwitcheMatrix selectedCell] intValue] != 2)
{
return self;
}
for (i = 0; i < [prefFileSwitcheMatrix cellCount]; i++)
{
id aCell = [prefFileSwitcheMatrix cellAt:i:0];
if (([aCell state] == 2) && ([prefFileSwitcheMatrix selectedCell] != aCell))
{
[aCell setState:1];
[aCell display];
}
}
return self;
}
@end /* Preferences_Methods */
#import <misckit/MiscMailApp.h>
#import <appkit/Panel.h>
/* Methods to handle mailing. */
@implementation CalendarController (MailHandler)
- droppingLine:sender
{
id addressString = [[MiscString alloc] initString:
"rmyers@dec5200.acs.uci.edu"],
subjectString = [[MiscString alloc] initString:
"Note RE: kfc.app"],
messageString = [[MiscString alloc] initString:
"Kurt, great app.\nThought you might like to know that..."];
if (myMailer == nil)
{
myMailer = [MiscMailApp localMailer];
}
if (myMailer == nil)
{
}
else
{
[myMailer sendMailTo:addressString subject:subjectString
body:messageString];
}
[addressString free];
[subjectString free];
[messageString free];
return self;
}
- bugReport:sender
{
id addressString = [[MiscString alloc] initString:
"rmyers@dec5200.acs.uci.edu"],
subjectString = [[MiscString alloc] initString:
"BUG IN kfc.app"],
messageString = [[MiscString alloc] initString:
"Kurt, kfc.app blew chunks when I was:\n"];
if (myMailer == nil)
{
myMailer = [MiscMailApp localMailer];
}
if (myMailer == nil)
{
NXRunAlertPanel ("MailApp Failure",
"The mail connection has failed.\nIt may be because you quit Mail recently.\nYou may restart kfc.app, or mail kurt at rmyers@dec5200.acs.uci.edu",
NULL, NULL, NULL);
}
else
{
[myMailer sendMailTo:addressString subject:subjectString
body:messageString];
}
[addressString free];
[subjectString free];
[messageString free];
return self;
}
- hireMe:sender
{
id addressString = [[MiscString alloc] initString:
"rmyers@dec5200.acs.uci.edu"],
subjectString = [[MiscString alloc] initString:
"kfc.app referral"],
messageString = [[MiscString alloc] initString:
"Kurt,\nkfc.app is a nice product.\nI know someone who could use a programmer like you.\n"];
if (myMailer == nil)
{
myMailer = [MiscMailApp localMailer];
}
if (myMailer == nil)
{
}
else
{
[myMailer sendMailTo:addressString subject:subjectString
body:messageString];
}
[addressString free];
[subjectString free];
[messageString free];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.