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.