This is ClientInfo.m in view mode; [Download] [Up]
/* * For legal stuff see the file COPYRIGHT */ #import <stdlib.h> #import <objc/hashtable.h> #import "ClientInfo.h" #import "Controller.h" #import "Session.h" #import "Expense.h" @interface ClientInfo(PRIVATE) - (void)setupSessionList; - (void)setupExpenseList; @end @implementation ClientInfo - init { [super init]; [self setupSessionList]; [self setupExpenseList]; return self; } - free { [[sessionList freeObjects] free]; sessionList = nil; [[expenseList freeObjects] free]; expenseList = nil; return [super free]; } - (void)computeTotalMins { int i, count = [sessionList count]; totalMins = 0; for ( i = 0; i < count; i++ ) totalMins += [[sessionList objectAt:i] minutes]; } /* * Called by the controller when a new session has been started. */ - addSession:session { [sessionList addObject:session]; totalMins += [session minutes]; return self; } - deleteSession:session { totalMins -= [session minutes]; return [sessionList removeObject:session]; } /* * Delete all session and expense data */ - (void)deleteSessionsAndExpenses { [sessionList freeObjects]; [expenseList freeObjects]; totalMins = 0.0; } - (const char *)lastDescription { const char *str = [[sessionList objectAt:0] description]; return str ? str : ""; } - (int)sessionCount { return [sessionList count]; } - sessionAt:(int)position { return [sessionList objectAt:position]; } /* * Called by the controller when a new session has been started. */ - addExpense:expense { [expenseList addObject:expense]; return self; } - deleteExpense:expense { return [expenseList removeObject:expense]; } - (void)deleteAllExpenses { [expenseList freeObjects]; } - (int)expenseCount { return [expenseList count]; } - expenseAt:(int)position { return [expenseList objectAt:position]; } - (void)sortSessions { [sessionList sort]; } - (void)sortExpenses { [expenseList sort]; } - (const char *)shortName { return shortName; } - (const char *)clientName { return clientName; } - (const char *)contactName { return contactName; } - (const char *)street { return street; } - (const char *)city { return city; } - (const char *)addrState /* "state" is a defined method in Cell...*/ { return state; } - (const char *)zipCode { return zipCode; } - (const char *)faxNumber { return faxNumber; } - (const char *)emailAddr { return emailAddr; } - (float)hourlyRate { return hourlyRate; } - (int)totalMins { return totalMins; } /* * This is necessary to have the number we use in calculations be * (almost) identical to that which is printed on the invoices. It * turns out for a total number of hours such as 76.466667, which * prints and displays as 76.47 causes enough error when multiplied * by an hourly rate that clients noticed the difference. (It happened * to be in their favor. This adjusts it to remove the discrepancy. * It happens to result in a few more cents in our favor, by the way! */ - (float)totalHours { return ((int)((totalMins * 100)/60 + 0.5))/100.0; /* round up then truncate */ } - (float)totalBillable { return hourlyRate * [self totalHours]; } /* * Compute this on the fly. */ - (float)totalExpenses { int i, count = [expenseList count]; float amount = 0.0; for ( i = 0; i < count; i++ ) { Expense *expense = [expenseList objectAt:i]; amount += [expense amount]; } return amount; } - setShortName:(const char *)str { freeAndCopy( &shortName, str ); return self; } - setClientName:(const char *)str { freeAndCopy( &clientName, str ); return self; } - setContactName:(const char *)str { freeAndCopy( &contactName, str ); return self; } - setStreet:(const char *)str { freeAndCopy( &street, str ); return self; } - setCity:(const char *)str { freeAndCopy( &city, str ); return self; } - setAddrState:(const char *)str { freeAndCopy( &state, str ); return self; } - setZipCode:(const char *)str { freeAndCopy( &zipCode, str ); return self; } - setFaxNumber:(const char *)str { freeAndCopy( &faxNumber, str ); return self; } - setEmailAddr:(const char *)str { freeAndCopy( &emailAddr, str ); return self; } - setHourlyRate:(float)value { hourlyRate = value; return self; } - setTotalMins:(int)value { totalMins = value; return self; } /* * Compress out consecutive sessions with identical descriptions. * This method assumes the list stores sessions in reverse * chronological order. */ - (void)compactSessions { int i, earliestPos = [sessionList count] - 1; Session *earliest = [sessionList objectAt:earliestPos]; for ( i = earliestPos - 1; i >= 0; --i ) { Session *session; session = [sessionList objectAt:i]; /* Check for consecutive sessions on the same date with the same text */ if ( strcmp([session description], [earliest description]) == 0 && strcmp([session startDateString], [earliest startDateString]) == 0 ) { [earliest setMinutes:[earliest minutes] + [session minutes]]; [[sessionList removeObjectAt:i] free]; } else earliest = session; /* this is our new base case */ } } - (void)exportToFile:(FILE *)fp { int i, count = [sessionList count]; for ( i = 0; i < count; i++ ) { Session *session; session = [sessionList objectAt:i]; fprintf( fp, "%s%c%s%c%s%c%d%c%s\n", shortName, DELIMITER, [session startDateString], DELIMITER, [session startTimeString], DELIMITER, [session minutes], DELIMITER, [session description] ); } } - read: (NXTypedStream *) stream { extern int FileVersion; switch ( FileVersion ) { case 0: NXReadTypes( stream, "********f", &clientName, &contactName, &street, &city, &state, &zipCode, &faxNumber, &emailAddr, &hourlyRate ); /* make the short name the same as the full name initially */ shortName = NXCopyStringBuffer(clientName); break; default: /* Version 1 added shortName for client */ NXReadTypes( stream, "*********f", &shortName, &clientName, &contactName, &street, &city, &state, &zipCode, &faxNumber, &emailAddr, &hourlyRate ); break; } if ( ! sessionList ) [self setupSessionList]; [sessionList read:stream]; [sessionList sort]; if ( ! expenseList ) [self setupExpenseList]; if ( FileVersion >= 2 ) { [expenseList read:stream]; [expenseList sort]; } [self computeTotalMins]; return self; } - write:(NXTypedStream *) stream { NXWriteTypes( stream, "*********f", &shortName, &clientName, &contactName, &street, &city, &state, &zipCode, &faxNumber, &emailAddr, &hourlyRate ); [sessionList write:stream]; [expenseList write:stream]; return self; } @end @implementation ClientInfo(PRIVATE) /* * Compare the dates (and if equal, the times) of two * sessions and return the proper value to achieve a * reverse chronological sort. */ - (int)compareSessions:(Session *)obj1 :(Session *)obj2 { int date1 = [obj1 dateValue], date2 = [obj2 dateValue]; if ( date1 == date2 ) return ( [obj1 timeValue] > [obj2 timeValue] ? -1 : 1 ); return ( date1 > date2 ? -1 : 1 ); } - (int)compareExpenses:(Expense *)obj1 :(Expense *)obj2 { int date1 = [obj1 dateValue], date2 = [obj2 dateValue]; return ( date1 > date2 ? -1 : 1 ); } - (void)setupSessionList { if ( ! sessionList ) { sessionList = [[SortList alloc] init]; [sessionList setDelegate:self]; [sessionList setAutoSort:YES]; [sessionList useComparisonMethod:@selector(compareSessions::)]; } } - (void)setupExpenseList { if ( ! expenseList ) { expenseList = [[SortList alloc] init]; [expenseList setDelegate:self]; [expenseList setAutoSort:YES]; [expenseList useComparisonMethod:@selector(compareExpenses::)]; } } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.