Spell ecode with a lower-case c.
[MacTF.git] / DataHandler.m
blob214ef9bac66bb75f690b4c1d20d21843373e15ca
1 // MacTF Copyright 2004 Nathan Oates
3 /* 
5  * This source code is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Public License as published
7  * by the Free Software Foundation; either version 2 of the License,
8  * or (at your option) any later version.
9  *
10  * This source code is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * Please refer to the GNU Public License for more details.
14  *
15  * You should have received a copy of the GNU Public License along with
16  * this source code; if not, write to:
17  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
20 /* This file was modified by Kalle Olavi Niemitalo on 2007-10-18.  */
23 #import "DataHandler.h"
24 #import "UIElements.h"
26 @implementation DataHandler
28 #pragma mark -
29 #pragma mark Startup and Shutdown
31 - (id) init
33     if (self = [super init])
34     {
35         fileList = [[NSMutableArray alloc] init];
36                 searchList = [[NSMutableArray alloc] init];
37     }
38     return self;
41 - (void) dealloc
43     [fileList release];
44     [searchList release];
45     [super dealloc];
50 #pragma mark -
51 #pragma mark Simple Accessors
53 - (NSMutableArray *) fileList
55     return fileList;
58 - (NSMutableArray *) displayedList
60     if (searchIsActive) return searchList;
61         else return fileList;
64 - (void) setFileList: (NSArray *)newFileList
66     if (fileList != newFileList)
67     {
68         [fileList autorelease];
69         fileList = [[NSMutableArray alloc] initWithArray: newFileList];
70     }
73 - (void) setSearchList: (NSArray *)newSearchList
75     if (searchList != newSearchList)
76     {
77         [searchList autorelease];
78         searchList = [[NSMutableArray alloc] initWithArray: newSearchList];
79     }
82 -(void)awakeFromNib
84         searchIsActive = NO;
89 -(id)tableView:(NSTableView *)aTableView
90 objectValueForTableColumn:(NSTableColumn *)aTableColumn
91            row:(int)rowIndex
93     id theRecord, theValue;
94         // FIXME: Because DataHandler is used simultaneously from two threads,
95         // it sometimes happens that the NSMutableArray has already been emptied
96         // even though it was not empty when numberOfRowsInTableView was called.
97         // As a workaround, catch the resulting NSRangeException.
98         // This is not really safe and should be fixed by making the TFUSBController
99         // send results to the UI thread, which would then update the table data
100         // at its leisure.
101         @try {
102                 if (searchIsActive)     theRecord = [searchList objectAtIndex:rowIndex];
103                 else theRecord = [fileList objectAtIndex:rowIndex];
104                 theValue = [theRecord objectForKey:[aTableColumn identifier]];
105         } @catch (NSException* exception) {
106                 NSLog(@"DataHandler hit an exception: %@", exception);
107                 if ([[exception name] isEqualToString:NSRangeException])
108                         theValue = nil;
109                 else
110                         @throw;
111         }
112     return theValue;
115 // just returns the number of items we have.
116 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
118         if (searchIsActive) return [searchList count];
119     else return [fileList count];
122 - (void) reloadTable {
123     [tableView reloadData];
126 - (IBAction) search:(id)sender {
127         NSString *searchString = [sender stringValue];
128         if ([searchString isEqualToString:@""]) {
129                 searchIsActive = NO;
130                 [self reloadTable];
131         } else {
132                 NSArray *searchTerms = [searchString componentsSeparatedByString:@" "];
133                 NSEnumerator *enumerator = [searchTerms objectEnumerator];
134                 id anObject;
135                 BOOL firstToken = YES;
136                 NSArray *results = [[NSArray alloc] init];
137                 while (anObject = [enumerator nextObject]) {
138                         if (![anObject isEqualTo:@""]) {
139                                 NSPredicate *pred = [NSPredicate predicateWithFormat:@"(name contains[cd] %@)", anObject];
140                                 if (firstToken) 
141                                         results = [fileList filteredArrayUsingPredicate:pred];
142                                 else
143                                         results = [results filteredArrayUsingPredicate:pred];
144                                 firstToken = NO;
145                         }
146                 }
147                 [self setSearchList:results];
148                 searchIsActive = YES; 
149                 [self reloadTable];
150         }
154 // Drag n Drop methods
155 /*- (BOOL)tableView:(NSTableView *)aTableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard {
156      id list;
157         if (searchIsActive) 
158                 list = searchList;
159         else
160                 list = seqs;
161         NSMutableArray *tempArray = [NSMutableArray array];
162     movedRows = [[NSMutableArray alloc] init];
163     NSNumber *index;
164     id tempObject;
165     NSEnumerator *enumerator = [rows objectEnumerator];
166     while ( (index = [enumerator nextObject]) ) {
167         tempObject = [list objectAtIndex:[index intValue]];
168         [tempArray addObject:tempObject];
169     }
170     movedRows = tempArray;
171     NSData *data = [NSArchiver archivedDataWithRootObject: tempArray];
172     [pboard declareTypes: [NSArray arrayWithObject: @"NSGeneralPboardType"] owner: nil];
173     [pboard setData: data forType: @"NSGeneralPboardType"];
174     return YES;
177 - (BOOL)tableView:(NSTableView*)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation {
178         NSPasteboard *pboard = [info draggingPasteboard];
179     NSArray *types = [NSArray arrayWithObjects:@"NSFilenamesPboardType", nil];
180         NSString *desiredType = [pboard availableTypeFromArray:types];
181         if (nil == [info draggingSource]) // From other application
182     {
183                 NSData *carriedData = [pboard dataForType:desiredType];
184                 if (nil == carriedData)
185                 {
186                         //the operation failed for some reason
187                         NSRunAlertPanel(@"Paste Error", @"Sorry, but the paste operation failed", nil, nil, nil);
188                         return NO;
189                 } else {
190                         if ([desiredType isEqualToString:NSFilenamesPboardType]) {
191                                 //we have a list of file names in an NSData object
192                                 NSArray *fileArray = [pboard propertyListForType:@"NSFilenamesPboardType"]; //be caseful since this method returns id. We just happen to know that it will be an array.
193                                 NSEnumerator* selected = [fileArray objectEnumerator];
194                                 id currentSelected;
195                                 while (currentSelected = [selected nextObject]) {
196                                         NSLog(@"Upload:%@ to %@", currentSelected, [NSString stringWithString:[UIEl currentPath]]);
197                                         [UIEl uploadPath:currentSelected toPath:[NSString stringWithString:[UIEl currentPath]]];
198                                 }
199                         } else {
200                                 //this can't happen
201                                 NSAssert(NO, @"This can't happen");
202                                 return NO;
203                         }
204                         return YES;
205                 }
206     } else if (aTableView == [info draggingSource]) { // From self
207         return YES;        
208     }
209     // From other documents
210         return YES;    
213     - (NSDragOperation) tableView: (NSTableView *) view
214 validateDrop: (id <NSDraggingInfo>) info
215 proposedRow: (int) row
216 proposedDropOperation: (NSTableViewDropOperation) operation
217     {
218         
219         if (nil == [info draggingSource]) // From other application
220     {
221                 [view setDropRow: row dropOperation: NSTableViewDropAbove];
222         return NSDragOperationCopy;
223     }
224     else if (tableView == [info draggingSource]) // From self
225     {
226          return NSDragOperationNone;
227     }
228         return NSDragOperationNone;
231 // Delegate methods for tableview 
233 - (NSMutableDictionary*) newTFFileFromSwappedHexData:(NSData*) input {
234         
235         NSData* mjdHex = [input subdataWithRange:(NSRange) {0,2}];
236         NSData* hrHex = [input subdataWithRange:(NSRange) {2,1}];
237         NSData* minHex = [input subdataWithRange:(NSRange) {3,1}];
238         NSData* secHex = [input subdataWithRange:(NSRange) {4,1}];
239         NSData* typeHex = [input subdataWithRange:(NSRange) {5,1}];
240         NSData* sizeMSBHex = [input subdataWithRange:(NSRange) {6,4}];
241         NSData* sizeLSBHex = [input subdataWithRange:(NSRange) {10,4}];
242         NSData* nameHex = [input subdataWithRange:(NSRange) {14,95}];
243         // windows attributes here?
244         
245         unsigned char c;
246         UInt16 u16_bigendian;
247         SInt32 s32_bigendian;
248         NSMutableDictionary* tfFile = [[NSMutableDictionary alloc] init];
249         [mjdHex getBytes:&u16_bigendian];
250         [tfFile setObject:[NSNumber numberWithInt:EndianU16_BtoN(u16_bigendian)] forKey:@"rawMJD"];
251         [hrHex getBytes:&c];
252         [tfFile setObject:[NSNumber numberWithChar:c] forKey:@"hour"];
253         [minHex getBytes:&c];
254         [tfFile setObject:[NSNumber numberWithChar:c] forKey:@"min"];
255         [secHex getBytes:&c];
256         [tfFile setObject:[NSNumber numberWithChar:c] forKey:@"sec"];
257         [typeHex getBytes:&c];
258         [tfFile setObject:[NSNumber numberWithChar:c] forKey:@"type"];
259         [sizeMSBHex getBytes:&s32_bigendian];
260         [tfFile setObject:[NSNumber numberWithLong:EndianS32_BtoN(s32_bigendian)] forKey:@"sizeMSB"];
261         [sizeLSBHex getBytes:&s32_bigendian];
262         [tfFile setObject:[NSNumber numberWithLong:EndianS32_BtoN(s32_bigendian)] forKey:@"sizeLSB"];
263         NSString* name = [[[NSString alloc] initWithData:nameHex encoding:NSISOLatin1StringEncoding] autorelease];
264         name = [name substringToIndex:[name rangeOfCharacterFromSet:[NSCharacterSet characterSetWithRange:(NSRange){0,1}]].location]; //trim anything past the 0x00
265         name = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithRange:(NSRange){5,1}]]; //remove extraneous 0x05 on some peoples names.
266         [tfFile setObject:name forKey:@"name"];
267 //      NSLog([tfFile description]);
268         return tfFile;
271 - (NSDictionary*) extractAttributes:(NSDictionary*)fattrs {
272         NSMutableDictionary* tfFile = [[NSMutableDictionary alloc] init];
273         //dates
274         NSCalendarDate* date = [[NSCalendarDate alloc] initWithTimeInterval:0 sinceDate:[fattrs fileModificationDate]];
275         int min = [date minuteOfHour];
276         int sec = [date secondOfMinute];
277         int hour = [date hourOfDay];
278         int MJDdate;
279         NSCalendarDate *mjdref = [[[NSCalendarDate alloc] initWithYear:1858 month:11 day:17 hour:0 minute:0 second:0 timeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]] autorelease];
280         [date years:NULL months:NULL days:&MJDdate hours:NULL minutes:NULL seconds:NULL sinceDate:mjdref];
281         [tfFile setObject:[NSNumber numberWithInt:MJDdate] forKey:@"rawMJD"];
282         [tfFile setObject:[NSNumber numberWithInt:hour] forKey:@"hour"];
283         [tfFile setObject:[NSNumber numberWithInt:min] forKey:@"min"];
284         [tfFile setObject:[NSNumber numberWithInt:sec] forKey:@"sec"];
285         
286         //types
287         NSString* type = [fattrs fileType];
288         if ([type isEqualToString:@"NSFileTypeDirectory"]) 
289                 [tfFile setObject:[NSNumber numberWithInt:1] forKey:@"type"];
290         else
291                 [tfFile setObject:[NSNumber numberWithInt:2] forKey:@"type"];
292         
293         //sizes
294         long long size = [fattrs fileSize];
295         [tfFile setObject:[NSNumber numberWithLong:size/0xFFFFFFFF] forKey:@"sizeMSB"];
296         [tfFile setObject:[NSNumber numberWithLong:size%0xFFFFFFFF] forKey:@"sizeLSB"];
297         return tfFile;
300 -(NSData*) getDataFromTFFile:(NSDictionary*) infile withName:(NSString*) inputName {
301         UInt16 u16_bigendian = EndianU16_NtoB([[infile objectForKey:@"rawMJD"] shortValue]);
302         NSMutableData* build = [NSMutableData dataWithBytes:&u16_bigendian length:2];
303         char c = [[infile objectForKey:@"hour"]charValue];
304         [build appendData:[NSData dataWithBytes:&c length:1]];
305         c = [[infile objectForKey:@"min"] charValue];
306         [build appendData:[NSData dataWithBytes:&c length:1]];
307         c = [[infile objectForKey:@"sec"] charValue];
308         [build appendData:[NSData dataWithBytes:&c length:1]];
309         c = [[infile objectForKey:@"type"] charValue];
310         [build appendData:[NSData dataWithBytes:&c length:1]];
311         SInt32 s32_bigendian = EndianS32_NtoB([[infile objectForKey:@"sizeMSB"] longValue]);
312         [build appendData:[NSData dataWithBytes:&s32_bigendian length:4]];
313         s32_bigendian = EndianS32_NtoB([[infile objectForKey:@"sizeLSB"] longValue]);
314         [build appendData:[NSData dataWithBytes:&s32_bigendian length:4]];
315         NSData * name = [inputName dataUsingEncoding:NSISOLatin1StringEncoding];
316         [build appendData:name];
317         if ([name length] < 95)
318                 [build increaseLengthBy:95-[name length]];
319         // other file stuff (5 bytes) - maybe add this later?
320         [build increaseLengthBy:5];
321         return build;
324 - (void) convertRawDataToUseful:(NSMutableDictionary*) input {
325         
326         //turn MJD and time into a useful object
327         if ([[input objectForKey:@"type"] intValue] == 1) { //folder
328                 [input setObject:@"" forKey:@"date"];
329                 [input setObject:[NSDate distantPast] forKey:@"sortdate"];
330                 [input setObject:[NSImage imageNamed:@"folder.tif"] forKey:@"icon"];
331                 if ([[input objectForKey:@"name"] isEqualToString:@".."]) 
332                         [input setObject:@".. (Parent folder)" forKey:@"name"];
333                 [input setObject:@"0000" forKey:@"suffix"];
334         } else {
335                 if ([[input objectForKey:@"name"] hasSuffix:@".tgd"]) [input setObject:[NSImage imageNamed:@"tgd.tif"] forKey:@"icon"];
336                 else if ([[input objectForKey:@"name"] hasSuffix:@".tap"]) [input setObject:[NSImage imageNamed:@"package.tif"] forKey:@"icon"];
337                 else if ([[input objectForKey:@"name"] hasSuffix:@".mp3"]) [input setObject:[NSImage imageNamed:@"mp3.tif"] forKey:@"icon"];
338                 else if ([[input objectForKey:@"name"] hasSuffix:@".txt"] || [[input objectForKey:@"name"] hasSuffix:@".ini"]) [input setObject:[NSImage imageNamed:@"txt.tif"] forKey:@"icon"];
339                 else [input setObject:[NSImage imageNamed:@"file.tif"] forKey:@"icon"];
340                 [input setObject:[[input objectForKey:@"name"] pathExtension] forKey:@"suffix"];
341                 
342                 int daysSinceRef = [[input objectForKey:@"rawMJD"] intValue] - 51910; // ref date is 1/1/2001 51911??
343                 float secsSinceRef = (((daysSinceRef * 24 + [[input objectForKey:@"hour"] intValue])* 60 + [[input objectForKey:@"min"] intValue]) * 60); 
344                 NSCalendarDate* tempDate = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:secsSinceRef];
345                 //now adjust for timezone
346                 int offset = [[NSTimeZone defaultTimeZone] secondsFromGMTForDate:tempDate];
347                 //NSLog(@"TZ offset:%i td:%@", offset, [tempDate description]);
348                 NSCalendarDate* finalDate = [tempDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-1*offset]; 
349                 //NSLog(@"TZ td:%@", [finalDate description]);
350                 [input setObject:finalDate forKey:@"date"];
351                 [input setObject:finalDate forKey:@"sortdate"];
352         }
354         //turn size fields into a proper number
355         double size;
356         double big =  pow( 256., 4. );
357         double lsb = [[input objectForKey:@"sizeLSB"] doubleValue];
358         double msb = [[input objectForKey:@"sizeMSB"] doubleValue];
359         if (lsb < 0) 
360                 lsb = lsb + pow( 256., 4. ); //rollover
361         if (msb < 0) 
362                 msb = msb + pow( 256., 4. ); //rollover
363         size = msb * big + lsb;
364         [input setObject:[NSNumber numberWithDouble:size] forKey:@"fileSize"];
365         
366         NSString* ret = nil;
367         if( size == 0. ) ret = NSLocalizedString( @" - ", "no file size" );
368         else if( size > 0. && size < 1024. ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.0f B", "file size measured in bytes" ), size];
369         else if( size >= 1024. && size < pow( 1024., 2. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.1f KB", "file size measured in kilobytes" ), ( size / 1024. )];
370         else if( size >= pow( 1024., 2. ) && size < pow( 1024., 3. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.2f MB", "file size measured in megabytes" ), ( size / pow( 1024., 2. ) )];
371         else if( size >= pow( 1024., 3. ) && size < pow( 1024., 4. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.2f GB", "file size measured in gigabytes" ), ( size / pow( 1024., 3. ) )];
372         else if( size >= pow( 1024., 4. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.4f TB", "file size measured in terabytes" ), ( size / pow( 1024., 4. ) )];
373         else ret = NSLocalizedString( @" ? ", "unknown file size" );
374         [input setObject:ret forKey:@"size"];
377 - (NSDictionary*) extractDataFromRecHeader:(NSString*)file forModel:(NSString*)model {
378         NSMutableDictionary* extracts = [NSMutableDictionary dictionaryWithCapacity:8];
379         NSData* fileData = [NSData dataWithContentsOfFile:file];
380         
381         int modelOffset = 0;
382         if ([model isEqualToString:@"TF5800"]) modelOffset = 4;
383         unsigned char c; //1byte
384         UInt16 u16_bigendian;
385         [[fileData subdataWithRange:(NSRange) {8,2}] getBytes:&u16_bigendian];
386         [extracts setObject:[NSNumber numberWithShort:EndianU16_BtoN(u16_bigendian)] forKey:@"duration"];
387         NSString* channel = [[NSString alloc] initWithData:[fileData subdataWithRange:(NSRange) {28,24}] encoding:NSISOLatin1StringEncoding];
388         NSRange r = [channel rangeOfCharacterFromSet:[NSCharacterSet characterSetWithRange:(NSRange){0,1}]]; //trim anything past the 0x00
389         if (r.location != NSNotFound)
390                 channel = [channel substringToIndex:r.location];        
391         channel = [channel stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithRange:(NSRange){5,1}]]; //remove extraneous 0x05 on some peoples names.
392         [extracts setObject:channel forKey:@"channel"];
393         [[fileData subdataWithRange:(NSRange) {76+modelOffset,2}] getBytes:&u16_bigendian];
394         [extracts setObject:[NSNumber numberWithInt:EndianU16_BtoN(u16_bigendian)] forKey:@"rawMJD"];
395         int daysSinceRef = [[extracts objectForKey:@"rawMJD"] intValue] - 51910; // ref date is 1/1/2001 51911??
396         [[fileData subdataWithRange:(NSRange) {78+modelOffset,1}] getBytes:&c];
397         NSNumber* hour = [NSNumber numberWithChar:c];
398         [[fileData subdataWithRange:(NSRange) {79+modelOffset,1}] getBytes:&c];
399         NSNumber* min = [NSNumber numberWithChar:c];
400         float secsSinceRef = (((daysSinceRef * 24 + [hour intValue])* 60 + [min intValue]) * 60); 
401         NSCalendarDate* finalDate = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:secsSinceRef];
402         [extracts setObject:finalDate forKey:@"startTime"];
403         [[fileData subdataWithRange:(NSRange) {85+modelOffset,1}] getBytes:&c];
404         NSString* name = [[NSString alloc] initWithData:[fileData subdataWithRange:(NSRange) {87+modelOffset,c}] encoding:NSISOLatin1StringEncoding];
405         [extracts setObject:name forKey:@"name"];
406         NSString* desc = [[NSString alloc] initWithData:[fileData subdataWithRange:(NSRange) {87+c+modelOffset,255}] encoding:NSISOLatin1StringEncoding];
407         r = [desc rangeOfCharacterFromSet:[NSCharacterSet characterSetWithRange:(NSRange){0,1}]]; //trim anything past the 0x00
408         if (r.location != NSNotFound)
409                 desc = [desc substringToIndex:r.location];
410         desc = [desc stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithRange:(NSRange){5,1}]]; //remove extraneous 0x05 on some peoples names.
411         [extracts setObject:desc forKey:@"description"];
412         [[fileData subdataWithRange:(NSRange) {362+modelOffset,2}] getBytes:&u16_bigendian];
413         NSString* extInfo = [[NSString alloc] initWithData:[fileData subdataWithRange:(NSRange) {369+modelOffset,EndianU16_BtoN(u16_bigendian)}] encoding:NSISOLatin1StringEncoding];
414         [extracts setObject:extInfo forKey:@"extInfo"];
415         //NSData* movieData = [fileData subdataWithRange:(NSRange) {1656,([fileData length]-1656)}];
416         //[movieData writeToFile:@"/Volumes/Tyr/cazlar/testMovie.ts" atomically:YES];
417         //NSLog([extracts description]);
418         return extracts;
423 @end