/* All Rights reserved */ #import "LedgerModel.h" @implementation LedgerModel - (void) dealloc { RELEASE(records); RELEASE(filteredRecords); RELEASE(defaultDate); RELEASE(dates); RELEASE(categories); [super dealloc]; } - (id) init { if( (self = [super init]) ) { records = [NSMutableArray new]; filteredRecords = [NSMutableArray new]; total = 0; new = -1; dates = [NSMutableArray new]; categories = [NSMutableArray new]; defaultDate = RETAIN([NSCalendarDate calendarDate]); return self; } return nil; } // Properties methods //******************** - (NSMutableArray *) records { return records; } - (void) setRecords: (NSMutableArray *) someRecords { if(someRecords) { ASSIGN(records, someRecords); [self _computeTotal]; [self _updateDates]; [self _updateCategories]; [[NSNotificationCenter defaultCenter] postNotificationName: @"Total Changed" object: nil]; } } - (NSCalendarDate *) defaultDate { return defaultDate; } - (void) setDefaultDate: (NSCalendarDate *) date { if(date) { ASSIGN(defaultDate, date); } } - (NSNumber *) total { return [NSNumber numberWithFloat: total]; } - (NSNumber *) average; { return [NSNumber numberWithFloat: average]; } - (NSArray *) dates { return [NSArray arrayWithArray: dates]; } - (NSArray *) categories; { return [NSArray arrayWithArray: categories]; } - (void) filterRecords: (NSDictionary *)theFilter { NSMutableArray *result = [NSMutableArray new]; NSString *date, *dateTo, *dateFrom; id tempCategories = [NSArray new]; int x = 0; int i; dateFrom = [NSString string]; dateTo = [NSString string]; // start with an empty array, and add in all the records whose date falls within the filter // if fromDate or toDate is empty add everything if( [[theFilter objectForKey: @"dateFrom"] isEqualToString: @"NONE"] || [[theFilter objectForKey: @"dateTo"] isEqualToString: @"NONE"] ) { for( i=0; i < [records count]; i++) [result addObject: [NSNumber numberWithInt: i]]; } else { dateFrom = [NSString stringWithString: [[NSCalendarDate dateWithString: [theFilter objectForKey: @"dateFrom"] calendarFormat: @"%d.%m.%Y"] descriptionWithCalendarFormat: @"%Y%m%d"]]; dateTo = [NSString stringWithString: [[NSCalendarDate dateWithString: [theFilter objectForKey: @"dateTo"] calendarFormat: @"%d.%m.%Y"] descriptionWithCalendarFormat: @"%Y%m%d"]]; for( i=0; i < [records count]; i++ ) { date = [[records objectAtIndex: i] objectForKey: @"dateSortable"]; if( [[[records objectAtIndex: i] objectForKey: @"forcedisplay"] isEqualToString: @"YES"] ) { [result addObject: [NSNumber numberWithInt: i]]; } else if( ([dateFrom compare: date] == NSOrderedSame || [dateFrom compare: date] == NSOrderedAscending) && ([dateTo compare: date] == NSOrderedSame || [dateTo compare: date] == NSOrderedDescending) ) { [result addObject: [NSNumber numberWithInt: i]]; } } } // if the filter is not NONE, remove from the array all the records // whose category is excluded if( [[theFilter objectForKey: @"categories"] isEqual: @"NONE"] ) { // don't remove any categories } else if( (tempCategories = [NSArray arrayWithArray: [theFilter objectForKey: @"categories"]]) ) { for( i = 0; i < [result count]; i++ ) { x = [[result objectAtIndex: i] intValue]; if( ![tempCategories containsObject: [[records objectAtIndex: x] objectForKey: @"category"]] && ![[[records objectAtIndex: x] objectForKey: @"forcedisplay"] isEqualToString: @"YES"] ) { [result removeObjectAtIndex: i]; i--; } } } // remove from the array all items that don't match the filtered item // or if the as yet non-existant 'exclude' switch box, is checked, remove // only the items that match. // not implemented: FIXME ASSIGN(filteredRecords, result); [self _computeTotal]; [[NSNotificationCenter defaultCenter] postNotificationName: @"Total Changed" object: nil]; } // Editing methods //***************** - (void) addRecord: (NSMutableDictionary *) record { if(record) { [record setObject: @"YES" forKey: @"forcedisplay"]; [records addObject: record]; [self _sortRecords]; [self _updateDates]; new = [records indexOfObject: record]; } } - (int) newRecord { int i; for( i=0; i < [filteredRecords count]; i++ ) { if( new == [[filteredRecords objectAtIndex: i] intValue] ) break; } return i; } - (void) clearNewRecord { // [[records objectAtIndex: anIndex] removeObjectForKey: @"new"]; new = -1; } // NSTableViewDataSource protocol methods //**************************************** - (int) numberOfRowsInTableView: (NSTableView *) view { return [filteredRecords count]; } - (id) tableView: (NSTableView *) view objectValueForTableColumn: (NSTableColumn *) column row: (int) row { int x; x = [[filteredRecords objectAtIndex: row] intValue]; return [[records objectAtIndex: x] objectForKey: [column identifier]]; } - (void) tableView: (NSTableView *) view setObjectValue: (id) object forTableColumn: (NSTableColumn *) column row: (int) row { int x = 0; x = [[filteredRecords objectAtIndex: row] intValue]; [[records objectAtIndex: x] setObject: object forKey: [column identifier]]; if( [[column identifier] isEqualToString: @"amount"] ) { if( new == x && [[records objectAtIndex: x] objectForKey: @"category"] && [[records objectAtIndex: x] objectForKey: @"item"] ) { [self clearNewRecord]; } [self _computeTotal]; [[NSNotificationCenter defaultCenter] postNotificationName: @"Total Changed" object: nil]; } if( [[column identifier] isEqualToString: @"category"] ) { [self _updateCategories]; if( [[[records objectAtIndex: x] objectForKey: @"forcedisplay"] isEqualToString: @"YES"] ) [[records objectAtIndex: x] removeObjectForKey: @"forcedisplay"]; } } // NSTableViewDataSource drag and drop methods //********************************************* // disabled for now ... // - (NSDragOperation) tableView: (NSTableView *) view // validateDrop: (id ) info // proposedRow: (int) row // proposedDropOperation: (NSTableViewDropOperation) operation //{ // if (row > [records count]) // return NSDragOperationNone; // // if (nil == [info draggingSource]) // From other application // { // return NSDragOperationNone; // } // //else if (tableView == [info draggingSource]) // From self // // { // // return NSDragOperationNone; // // } // else // { // [view setDropRow: row dropOperation: NSTableViewDropAbove]; // return NSDragOperationCopy; // } //} // //- (BOOL) tableView: (NSTableView *) view // acceptDrop: (id ) info // row: (int) row // dropOperation: (NSTableViewDropOperation) operation //{ // NSPasteboard *pboard = [info draggingPasteboard]; // NSData *data = [pboard dataForType: @"NSGeneralPboardType"]; // // if (row > [records count]) // return NO; // // if (nil == [info draggingSource]) // From other application // { // return NO; // } // //else if (tableView == [info draggingSource]) // From self // // { // // return NO; // // } // else // { // id object = [NSUnarchiver unarchiveObjectWithData: data]; // [records insertObject: object atIndex: row]; // //[tableView reloadData]; // //[self updateChangeCount: NSChangeDone]; // // return YES; // } // return NO; //} // //- (BOOL) tableView: (NSTableView *) view // writeRows: (NSArray *) rows // toPasteboard: (NSPasteboard *) pboard //{ // id object = [records objectAtIndex: [[rows lastObject] intValue]]; // NSData *data = [NSArchiver archivedDataWithRootObject: object]; // // [pboard declareTypes: [NSArray arrayWithObject: @"NSGeneralPboardType"] // owner: nil]; // [pboard setData: data forType: @"NSGeneralPboardType"]; // return YES; //} // Private methods and functions //******************************* - (void) _sortRecords { NSMutableArray *newArray; newArray = [NSMutableArray arrayWithArray: [records sortedArrayUsingFunction: _recordSort context: NULL]]; ASSIGN(records, newArray); } - (void) _computeTotal { // NSDecimalNumber *tempAverage; id temp; int i; int x = 0; float value = 0; for( i=0; i < [filteredRecords count]; i++ ) { x = [[filteredRecords objectAtIndex: i] intValue]; if( (temp = [[records objectAtIndex: x] objectForKey: @"amount"]) ) { value = value + [temp floatValue]; } } total = value; if ([dates count] >= 1) average = total/[dates count]; else average = 0; //tempAverage = [NSDecimalNumber numberWithFloat: average]; //tempAverage = [tempAverage decimalNumberByRoundingAccordingToBehavior: [NSDecimalNumber defaultBehavior]]; //average = [tempAverage floatValue]; } - (void) _updateDates { int i; dates = [[NSMutableArray alloc] initWithCapacity: 1]; for( i=0; i<[records count]; i++ ) { if( ![dates containsObject: [[records objectAtIndex: i] objectForKey: @"date"]] ) { [dates addObject: [[records objectAtIndex: i] objectForKey: @"date"]]; } } [[NSNotificationCenter defaultCenter] postNotificationName: @"Dates Changed" object: dates]; } - (void) _updateCategories; { int i; categories = [[NSMutableArray alloc] initWithCapacity: 1]; for( i=0; i<[records count]; i++ ) { if( ![categories containsObject: [[records objectAtIndex: i] objectForKey: @"category"]] && [[records objectAtIndex: i] objectForKey: @"category"] != nil && ![[[records objectAtIndex: i] objectForKey: @"category"] isEqualToString: @""] ) { [categories addObject: [[records objectAtIndex: i] objectForKey: @"category"]]; } } if( categories ) [[NSNotificationCenter defaultCenter] postNotificationName: @"Categories Changed" object: categories]; } int _recordSort(id rec1, id rec2, void *context) { NSString *s1 = (NSString *)[rec1 objectForKey: @"dateSortable"]; NSString *s2 = (NSString *)[rec2 objectForKey: @"dateSortable"]; if ( !s1 || !s2 ) { NSLog(@"we're missing some data to sort by"); return NSOrderedSame; } return [s1 compare: s2]; } @end