Don't link with Carbon.framework.
[MacTF.git] / UIElements.m
blob879ee26656580d2ec15d3675e7b7d8c867aeb4b5
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.  */
22 #import "UIElements.h"
23 #include <unistd.h>
25 @interface UIElements (PrivateMethods)
26 - (void) setCurrentPath:(NSString*)path;
27 @end
29 @implementation UIElements
31 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:
32         (NSApplication *)theApplication {
33     return YES;
36 - (BOOL)application:(NSApplication *)sender openFile:(NSString *)path
38         if (!connected) 
39                 [self connect:nil];
40         [self uploadPath:path toPath:[NSString stringWithString:currentPath]];
41         NSLog(@"DockUpload:%@", path);
42         return YES;
45 -(void) awakeFromNib {
46         [mainWindow setTitleBarHeight:22.0];
47         [mainWindow setBottomBarHeight:18.0];
48         [mainWindow setMidBarHeight: 32.0 origin:40.0];
49         connected = NO;
50         paused = NO;
51         highlightImageData = @"<4d4d002a 00000048 800f4f6d a2ca65ca 564b390a 69371941 1ee22622 dc04743b 7c86826e 900fcdb1 d9e5b237 3ab60647 06b0b8d8 d5151a1a 82732348 46616888 4bcd00f9 719f0100 000d0100 00030000 00010001 00000101 00030000 00010012 00000102 00030000 00030000 00ea0103 00030000 00010005 00000106 00030000 00010002 00000111 00040000 00010000 00080115 00030000 00010003 00000116 00040000 00010000 2aaa0117 00040000 00010000 003f011a 00050000 00010000 00f0011b 00050000 00010000 00f8011c 00030000 00010001 00000128 00030000 00010002 00000000 00000008 00080008 000afc80 00002710 000afc80 00002710 >";
52         [self setCurrentPath:@"\\"];
53         [currentlyField setStringValue:NSLocalizedString(@"IDLE", @"Idle")];
54         [currentlyField displayIfNeeded];
55         [tableView setDoubleAction: @selector(doubleClick:)];
56         [tableView setTarget: self];
57         [tableView registerForDraggedTypes: [NSArray arrayWithObjects: @"NSFilenamesPboardType", @"NSFilenamesPboardType", nil]];
58         tfusbc = [[TFUSBController alloc] init];
59         [tfusbc setDH:dh tableView:tableView]; //send needed items to tfusbc
60         sortAscending = TRUE;
61         [turboCB setState:0];
62         [turboCB setEnabled:NO];
63         [versionField setStringValue:[[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleVersion"]]; 
64         [pathBar setEnabled:YES];
65         [[pathBar cell] setSegmentCount:1];
66         [[pathBar cell] setLabel:@"Path:" forSegment:0];
67         [[pathBar cell] setWidth:0 forSegment:0];
68         [pathBar sizeToFit];
69         [[dh fileList] addObject:[NSDictionary dictionaryWithObject:NSLocalizedString(@"NOT_CONNECTED_ENTRY", @"Not connected") forKey:@"name"]];
70         [tableView reloadData];
71         prefs = [[NSUserDefaults standardUserDefaults] retain];
72         if ([prefs boolForKey:@"auto-connect"]) {
73                 [self connect:nil];
74                 [autoCB setState:1];    
75         }
78 - (BOOL)validateMenuItem:(NSMenuItem*)anItem {
79     if ([[anItem title] isEqualToString:NSLocalizedString(@"DL_MENUITEM", @"Download")] && (1 > [tableView numberOfSelectedRows])) {
80         return NO;
81     }
82         if ([[anItem title] isEqualToString:NSLocalizedString(@"DEL_MENUITEM", @"Delete")] && (1 > [tableView numberOfSelectedRows])) {
83         return NO;
84     }
85         if ([[anItem title] isEqualToString:NSLocalizedString(@"RN_MENUITEM", @"Rename")] && (1 != [tableView numberOfSelectedRows])) {
86         return NO;
87     }
88         if ([[anItem title] isEqualToString:NSLocalizedString(@"UL_MENUITEM", @"Upload")] && (! connected)) {
89         return NO;
90     }
91         if ([[anItem title] isEqualToString:NSLocalizedString(@"NF_MENUITEM", @"NewFolder")] && (! connected)) {
92         return NO;
93     }
94     return YES;
97 - (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
98     if ([[toolbarItem itemIdentifier] isEqual:@"ConnectTBIcon"]) {
99                 if (connected) {
100                         [toolbarItem setLabel:NSLocalizedString(@"DC_BUTTON", @"Disconnect")];
101                 } else {
102                         [toolbarItem setLabel:NSLocalizedString(@"CON_BUTTON", @"Connect")];
103                 }
104         }
105         if ([[toolbarItem itemIdentifier] isEqual:@"UploadTBIcon"] && (! connected)) {
106                 return NO;
107         }
108         if ([[toolbarItem itemIdentifier] isEqual:@"NewFolderTBIcon"] && (! connected)) {
109                 return NO;
110         }
111         if ([[toolbarItem itemIdentifier] isEqual:@"TurboTBIcon"] && (! connected)) {
112                 return NO;
113         }
114         if ([[toolbarItem itemIdentifier] isEqual:@"DownloadTBIcon"] && (1 > [tableView numberOfSelectedRows] || !connected)) {
115                 return NO;
116         }
117         if ([[toolbarItem itemIdentifier] isEqual:@"DeleteTBIcon"] && (1 > [tableView numberOfSelectedRows] || !connected)) {
118                 return NO;
119         }
120         if ([[toolbarItem itemIdentifier] isEqual:@"RenameTBIcon"] && (1 != [tableView numberOfSelectedRows] || !connected)) {
121                 return NO;
122         }
123     return YES;
127 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
129         [preview setString:@""];
130         if ([previewDrawer state] == NSDrawerClosedState) return; // don't bother getting previews if closed
131         if ([tableView numberOfSelectedRows] != 1 || !connected)
132                 return;
133         if ([tfusbc hasCurrentTransfer]) return; //for now, change to a priority transfer later?
134         id currentSelectedItem = [[dh displayedList] objectAtIndex:[tableView selectedRow]];
135         int type = [[currentSelectedItem objectForKey:@"type"] intValue];
136         if (type == 2) { //file
137                 NSString* nameOnToppy = [currentSelectedItem objectForKey:@"name"]; 
138                 if (([nameOnToppy hasSuffix:@".ini"] || [nameOnToppy hasSuffix:@".txt"] || [nameOnToppy hasSuffix:@".tgd"] || [nameOnToppy hasSuffix:@".tsv"] || [nameOnToppy hasSuffix:@".dat"] || [nameOnToppy hasSuffix:@".tap"])) {
139                         [drawerTabView selectTabViewItemAtIndex:0];
140                         NSString* tempFile = NSTemporaryDirectory(); // download preview to temp folder
141                         tempFile = [tempFile stringByAppendingPathComponent:@"MacTFTemp"];
142                         [tfusbc addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:currentSelectedItem,@"filename",currentPath,@"path",tempFile,@"savePath",[NSNumber numberWithUnsignedLongLong:0],@"offset",@"download",@"transferType",[NSNumber numberWithBool:NO],@"looping",[NSNumber numberWithInt:0],@"existingTime",nil] atIndex:-1];
143                         while ([tfusbc hasCurrentTransfer]) {
144                                 usleep(10);
145                         }
146                         [preview setString:[NSString stringWithContentsOfFile:tempFile]];
147                 } else if ([nameOnToppy hasSuffix:@".rec"]) { //display headers
148                         [drawerTabView selectTabViewItemAtIndex:1];
149                         NSString* tempFile = NSTemporaryDirectory(); // download first 64k to temp folder
150                         tempFile = [tempFile stringByAppendingPathComponent:@"MacTFTemp"];
151                         [tfusbc addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:currentSelectedItem,@"filename",currentPath,@"path",tempFile,@"savePath",[NSNumber numberWithUnsignedLongLong:0],@"offset",@"download",@"transferType",[NSNumber numberWithBool:NO],@"looping",[NSNumber numberWithInt:0],@"existingTime",nil] atIndex:-1];
152                         while ([tfusbc hasCurrentTransfer]) {
153                                 usleep(10);
154                         }
155                         NSDictionary* recData = [dh extractDataFromRecHeader:tempFile forModel:[prefs objectForKey:@"modelType"]]; //then parse and display headers
156                         [recStart setObjectValue:[recData objectForKey:@"startTime"]];
157                         [recDuration setObjectValue:[recData objectForKey:@"duration"]];
158                         [recDescription setStringValue:[recData objectForKey:@"description"]];
159                         [recName setStringValue:[recData objectForKey:@"name"]];
160                         [recExtInfo setStringValue:[recData objectForKey:@"extInfo"]];
161                         [recChannel setStringValue:[recData objectForKey:@"channel"]];
162                 }       
163                 else {
164                         [drawerTabView selectTabViewItemAtIndex:0];
165                         return;
166                 }
167         } else if (type == 1) { //folder
168                 [drawerTabView selectTabViewItemAtIndex:0];
169                 return;
170         } else {
171                 [statusField setStringValue:NSLocalizedString(@"SEL_ERROR", @"Selection error...")];
172                 return;
173         }
176 - (void) setAvailableSpace:(NSData*)input {
177         [availSpaceField setStringValue:[self prepForSizeDisplay:input]];
180 - (void) setFreeSpace:(NSData*) input {
181         [freeSpaceField setStringValue:[self prepForSizeDisplay:input]];
184 - (NSString*) prepForSizeDisplay:(NSData*) input {
185         UInt32 size_bigendian;
186         [input getBytes:&size_bigendian];
187         unsigned size = EndianU32_BtoN(size_bigendian);
188         NSString *ret = nil;
189                 if( size == 0. ) ret = NSLocalizedString( @" - ", "no file size" );
190                 else if( size > 0. && size < 1024. ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.0f KB", "file size measured in kilobytes" ), size];
191                 else if( size >= 1024. && size < pow( 1024., 2. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.1f MB", "file size measured in megabytes" ), ( size / 1024. )];
192                 else if( size >= pow( 1024., 2. ) && size < pow( 1024., 3. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.2f GB", "file size measured in gigabytes" ), ( size / pow( 1024., 2. ) )];
193                 else if( size >= pow( 1024., 3. ) && size < pow( 1024., 4. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.3f TB", "file size measured in terabytes" ), ( size / pow( 1024., 3. ) )];
194                 else if( size >= pow( 1024., 4. ) ) ret = [NSString stringWithFormat:NSLocalizedString( @"%.4f PB", "file size measured in pentabytes" ), ( size / pow( 1024., 4. ) )];
195         return ret;
198 - (IBAction) connect: (id) sender { 
199         if (! connected) {
200                 context = [tfusbc initializeUSB];
201                 if (context == nil) {
202                         [statusField setStringValue:NSLocalizedString(@"INIT_PROBLEM", @"Message when initialization fails.")];
203                         return;
204                 }
205                 // add a check here to see if really connected...
206                 connected = YES;
207                 [[dh fileList] removeAllObjects];
208                 [[dh fileList] addObject:[NSDictionary dictionaryWithObject:NSLocalizedString(@"FETCHING", @"Fetching") forKey:@"name"]];
209                 [tableView reloadData];
210                 [tfusbc clearQueues];
211                 paused = NO;
212                 [turboCB setState:0];
213                 [turboCB setEnabled:YES];
214                 [[pathBar cell] setSegmentCount:2];
215                 [[pathBar cell] setLabel:@"Path:" forSegment:0];
216                 [[pathBar cell] setLabel:@"\\" forSegment:1];
217                 [[pathBar cell] setWidth:0 forSegment:0];
218                 [[pathBar cell] setWidth:0 forSegment:1];
219                 [pathBar sizeToFit];
220                 [pathBar setHidden:NO];
221                 [connectLight setImage:[NSImage imageNamed:@"green.tiff"]];
222                 [connectButton setTitle:NSLocalizedString(@"DC_BUTTON", @"Disconnect.")];
223                 [statusField setStringValue:[NSString stringWithFormat:NSLocalizedString(@"CONNECTED_MESSAGE", @"Message giving connection speed."), [tfusbc getSpeed]]];
224                 [NSThread detachNewThreadSelector:@selector(transfer:) toTarget:tfusbc withObject:nil];
225         } else { // already connected
226                 connected = NO;
227                 if ([tfusbc hasCurrentTransfer]) { //gives a few seconds for busy thread to notice it should not be connected before cutting the device off
228                         sleep(3);
229                 }
230                 [tfusbc closeDevice:context];
231                 [connectButton setTitle:NSLocalizedString(@"CON_BUTTON", @"Connect.")];
232                 [statusField setStringValue:NSLocalizedString(@"NO_CONNECT", @"No connection.")];
233                 [[dh fileList] removeAllObjects];
234                 [tableView reloadData];
235                 [turboCB setState:0];
236                 [turboCB setEnabled:NO];
237                 [connectLight setImage:[NSImage imageNamed:@"red.tiff"]];
238                 [self setCurrentPath:@"\\"];
239                 return;
240         }
241         int ret = [self goToPath:@"\\"];
242         if (ret == 1) { 
243                 sleep (5); //error, wait a few secs for HDD to start if need be then try again
244                 [self goToPath:@"\\"];
245         }
246         [self setCurrentPath:@"\\"];
249 - (int) goToPath:(NSString*) path {
250         [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:path,@"path",@"fileList",@"transferType",nil]];
251         // FIXME: The caller would like to know whether the operation succeeded.
252         // But it has not even finished yet.  So goToPath should somehow notify
253         // the caller on completion.
254         return 0;
257 - (void)doubleClick:(id)sender
259         if ([tableView numberOfSelectedRows] != 1)
260                 return;
261         id currentSelectedItem = [[dh displayedList] objectAtIndex:[tableView selectedRow]];
262         int type = [[currentSelectedItem objectForKey:@"type"] intValue];
263         if (type == 2) { //file
264                 [self downloadFileDoubleClickThread:nil];
265         } else if (type == 1) { //folder
266                 NSString* currentName = [currentSelectedItem objectForKey:@"name"];
267                 if ([currentName isEqualToString:@".. (Parent folder)"]) { // move up
268                         NSMutableArray* array = [NSMutableArray arrayWithArray:[currentPath componentsSeparatedByString:@"\\"]];
269                         if ([array count] > 1){ // not going to root
270                                 [array removeLastObject];
271                                 NSString* temp = [NSString stringWithString:[array componentsJoinedByString:@"\\"]];
272                                 if ([self goToPath:temp]) {// if error
273                                         [self goToPath:temp]; // hackish workaround
274                                 }
275                                 int segCount = [pathBar segmentCount]; //remember count starts at 1, not zero!
276                                 NSRect r = [pathBar frame];
277                                 [[pathBar cell] setSegmentCount:segCount-1];
278                                 [pathBar setNeedsDisplayInRect:r];
279                                 [pathBar displayIfNeeded];
280                                 [self setCurrentPath:temp];
281                                 [statusField setStringValue:NSLocalizedString(@"CONNECTED_OK", @"Connection OK")];
282                         } else {//root
283                                 [self setCurrentPath:@"\\"];
284                                 [[pathBar cell] setSegmentCount:1];
285                         }
286                 } else { //move down
287                         NSMutableString* temp = [NSMutableString stringWithString:currentPath];
288                         if (![temp isEqualToString:@"\\"])
289                                         [temp appendString:@"\\"];
290                         [temp appendString:currentName];
291                         if ([self goToPath:temp]) { // if error, try again
292                                 [self goToPath:temp]; // hackish workaround
293                 }
294                         [self setCurrentPath:temp];
295                         int segCount = [pathBar segmentCount]; //remember count starts at 1, not zero!
296                         NSRect r = [pathBar frame];
297                         [[pathBar cell] setSegmentCount:segCount+1];
298                         [[pathBar cell] setLabel:currentName forSegment:segCount];
299                         [[pathBar cell] setWidth:0 forSegment:segCount];
300                         [pathBar sizeToFit];
301                         [pathBar displayIfNeededInRect:r];
302                         [statusField setStringValue:NSLocalizedString(@"CONNECTED_OK", @"Connection OK")];
303                 }
304         } else {
305                 [statusField setStringValue:NSLocalizedString(@"SEL_ERROR", @"Selection error...")];
306                 return; 
307         }
310 - (void) downloadFileDoubleClickThread:(NSDictionary*)fileInfo {
311         if (! connected) return;
312                 [self downloadSelectedFile:nil];
313 //      [self updateHDDSize];
316 /*- (void) updateHDDSize {
317         if (! connected) return;
318         NSData* hddSizeString = [tfusbc getHDDSize:context];
319         if (hddSizeString != nil) {
320                 [self setAvailableSpace:[hddSizeString subdataWithRange:(NSRange) {8,4}]];
321                 [self setFreeSpace:[hddSizeString subdataWithRange:(NSRange) {12,4}]];
322         } 
325 - (void)tableView:(NSTableView *)tView didClickTableColumn:(NSTableColumn *)tableColumn
327         // Either reverse the sort or change the sorting column
328         NSString* columnIdentifier = [tableColumn identifier];
329         BOOL newSortAscending = !(sortAscending && [tableColumn isEqualTo:selectedColumn]);
330         
331         NSString* sortKey = nil;
332         SEL sortSelector = @selector(compare:);
333         if ([columnIdentifier isEqualToString:@"name"]) {
334                 sortKey = @"name";
335                 sortSelector = @selector(caseInsensitiveCompare:);
336         } else if ([columnIdentifier isEqualToString:@"date"]) {
337                 sortKey = @"sortdate";
338         } else if ([columnIdentifier isEqualToString:@"size"]) {
339                 sortKey = @"fileSize";
340         } else if ([columnIdentifier isEqualToString:@"icon"]) {
341                 sortKey = @"suffix";
342         }
343         if (sortKey == nil)
344                 return;
346         NSSortDescriptor* descriptor = [[[NSSortDescriptor alloc]
347                         initWithKey:sortKey ascending:newSortAscending selector:sortSelector]
348                 autorelease];
349         NSArray* sortedArray = [[dh fileList] sortedArrayUsingDescriptors:
350                 [NSArray arrayWithObject:descriptor]];
351         NSImage* indicatorImage = [NSImage imageNamed:
352                 newSortAscending ? @"NSAscendingSortIndicator" : @"NSDescendingSortIndicator"];
354         sortAscending = newSortAscending;
355         selectedColumn = tableColumn;
356         [tView setIndicatorImage: indicatorImage inTableColumn: tableColumn];
357         // remove other indicators
358         NSMutableArray* otherColumns = [NSMutableArray arrayWithArray:[tView tableColumns]];
359         [otherColumns  removeObject:tableColumn];
360         NSEnumerator* cols = [otherColumns objectEnumerator];
361         id currentCol;
362         while (currentCol = [cols nextObject]) {
363                 [tView setIndicatorImage: nil inTableColumn: currentCol];
364         }
365         [dh setFileList:sortedArray];
366         [dh search:searchField];
367         [dh reloadTable];
371 - (IBAction) downloadSelectedFile:(id)sender{
372         if (! connected)
373                 return;
374         NSLog(@"Download file starting...");
375         NSEnumerator* selected = [tableView selectedRowEnumerator];
376         id currentSelected;
377         while (currentSelected = [selected nextObject]) {
378                 id currentSelectedItem = [[dh displayedList] objectAtIndex:[currentSelected intValue]];
379                 int type = [[currentSelectedItem objectForKey:@"type"] intValue];
380                 if (type == 1) {[statusField setStringValue:NSLocalizedString(@"NO_FOLDER_DL", @"Folder download not supported yet")];}
381                 else if (type == 2) {
382                         NSString* nameOnToppy = [currentSelectedItem objectForKey:@"name"];
383                         NSSavePanel *nssave = [[NSSavePanel savePanel]retain];
384                         int retButton = [nssave runModalForDirectory:nil file:nameOnToppy];
385                         if (retButton) {
386                                 NSString* savePath= [nssave filename]; 
387                                 NSLog(savePath);
388                                 unsigned long long offset = 0;
389                                 NSFileHandle *newFileHandle = [NSFileHandle fileHandleForReadingAtPath:savePath];
390                                 if (!(newFileHandle==nil)) { // there is a file there, ask if want to resume
391                                         NSLog(@"existing file length: %qu", [newFileHandle seekToEndOfFile]);
392                                         //ask to resume, save offset if yes
393                                         //offset = [newFileHandle seekToEndOfFile];
394                                 }
395                                 [tfusbc setProgressBar:progressBar time:progressTime turbo:turboCB];
396                                 [tfusbc addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:currentSelectedItem,@"filename",currentPath,@"path",savePath,@"savePath",[NSNumber numberWithUnsignedLongLong:offset],@"offset",@"download",@"transferType",[NSNumber numberWithBool:YES],@"looping",[NSNumber numberWithInt:0],@"existingTime",nil] atIndex:-1];
397                         }
398                 }               
399         }
400         //              [self updateHDDSize];
401         }
403 - (void) finishTransfer {
404         [currentlyField setStringValue:@""];
405         [progressBar setDoubleValue:0];
406         [[NSSound soundNamed:@"Ping"] play];
407         [connectLight setImage:[NSImage imageNamed:@"green.tiff"]];
408         [currentlyField setStringValue:NSLocalizedString(@"IDLE", @"Idle")];
409         [currentlyField display];
412 - (IBAction) uploadFile: (id) sender {
413         if (! connected)
414                 return;
415         NSOpenPanel *nsop = [[NSOpenPanel openPanel]retain];
416         [nsop setAllowsMultipleSelection:YES];
417         [nsop setCanChooseFiles:YES];
418         [nsop setCanChooseDirectories:YES];
419         int retButton = [nsop runModal];
420         NSArray *returnedNames = [nsop filenames];      
421         if (retButton == NSFileHandlingPanelCancelButton) {             
422                 NSLog(@"Upload cancelled");
423                 return;
424         }
425         //get info about the chosen file - later if do folders check here and do recursive all things in folder???
426         NSEnumerator *enumerator = [returnedNames objectEnumerator];
427         id returnedName;
428 //      [statusField setStringValue:[NSLocalizedString(@"CONNECTED_OK", @"Connection OK") stringByAppendingString:NSLocalizedString(@"DOWNLOAD", @"Download")]; 
429         [connectLight setImage:[NSImage imageNamed:@"blink.tiff"]];
430         while (returnedName = [enumerator nextObject]) {
431                 NSLog(returnedName);
432                 [self uploadPath:returnedName toPath:[NSString stringWithString:currentPath]];
433         }
438 - (void) uploadPath:(NSString*) path toPath:(NSString*) toPath {
439         if (! connected)
440                 return;
441         NSFileManager* fm = [NSFileManager defaultManager];
442         NSDictionary *fattrs = [fm fileAttributesAtPath:path traverseLink:NO];
443         if (!fattrs) {
444                 NSLog(@"Path of file to upload is incorrect!");
445                 return; }
446         NSString* type = [fattrs fileType];
447         if ([type isEqualToString:@"NSFileTypeDirectory"]) {
448                 // should look to create folder on Toppy, or add stuff if already there (maybe it does this automatically?), and place in subfolders etc
449                 [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:[path lastPathComponent],@"newName",toPath,@"path",@"newFolder",@"transferType", nil]];
450                 NSArray* subPaths = [fm directoryContentsAtPath:path];
451                 NSEnumerator* e = [subPaths objectEnumerator];
452                 id object;
453                 while (object = [e nextObject]) {
454                         NSString* subPath = [NSString stringWithFormat:@"%@/%@", path, object];
455                         NSString* subToPath = [NSString stringWithFormat:@"%@\\%@", toPath, [path lastPathComponent]];
456                 //      NSLog(@"subdir: %@, %@", subPath, subToPath);
457                         if (![object isEqualToString:@".DS_Store"]) {
458                                 [self uploadPath:subPath toPath:subToPath];
459                         }
460                 }
461                 if (connected) { //may have disconnected during threaded upload (although not actually threaded here!)
462         //              [self updateHDDSize];
463                         [self goToPath:currentPath];
464                 }
465         }
466         else {
467                 NSDictionary* fileAttr = [dh extractAttributes:fattrs];         // turn Dict into a new TypeFile dict
468                 NSMutableArray* array = [NSMutableArray arrayWithArray:[path componentsSeparatedByString:@"/"]]; // cut down name
469                 NSString* fileName = [NSString stringWithString:[array lastObject]];
470                 NSData* typeFile = [dh getDataFromTFFile:fileAttr withName:fileName];
471                 [progressBar setDoubleValue:0];
472                 [currentlyField setStringValue:[NSLocalizedString(@"UPLOADING", @"Uploading") stringByAppendingString:fileName]];
473                 [currentlyField displayIfNeeded];
474                 [tfusbc setProgressBar:progressBar time:progressTime turbo:turboCB];
475                 [tfusbc addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:path,@"filename",[NSNumber numberWithUnsignedLongLong:[fattrs fileSize]],@"fileSize",toPath,@"path",typeFile,@"attributes",@"upload",@"transferType",[NSNumber numberWithUnsignedLongLong:0],@"offset",[NSNumber numberWithInt:0],@"existingTime",nil] atIndex:-1];
476         }
479 -(IBAction)deleteFile:(id)sender
481         if (! connected)
482                 return;
483         if ( [tableView numberOfSelectedRows] == 0 )
484         return;
485         NSString *title = NSLocalizedString(@"WARNING", @"Warning!");
486     NSString *defaultButton = NSLocalizedString(@"DEL", @"Delete");
487     NSString *alternateButton = NSLocalizedString(@"NO_DEL", @"Don't Delete");
488     NSString *otherButton = nil;
489     NSString *message = NSLocalizedString(@"DEL_MESSAGE", @"Are you sure you want to delete the selected file(s)?");
491     NSBeep();
492     NSBeginAlertSheet(title, defaultButton, alternateButton, otherButton, mainWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, nil, message);
495 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
497     if ( returnCode == NSAlertDefaultReturn ) {
498                 NSLog(@"Deleting file(s) starting...");
499                 NSEnumerator* selected = [tableView selectedRowEnumerator];
500                 id currentSelected;
501                 while (currentSelected = [selected nextObject]) {
502                         id currentSelectedItem = [[dh displayedList] objectAtIndex:[currentSelected intValue]];
503                         [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:currentSelectedItem,@"file",currentPath,@"path",@"delete",@"transferType", nil]];
504                 }
505         //      [self updateHDDSize];
506                 [self goToPath:currentPath];
507                 //should check so don't delete root folders?
508         }
511 - (IBAction)openPrefsSheet:(id)sender
513         [NSApp beginSheet:prefsWindow
514            modalForWindow:mainWindow
515                 modalDelegate:self
516            didEndSelector:@selector(mySheetDidEnd:returnCode:contextInfo:)
517           contextInfo: nil];
518         return;  // leave without doing anything else
521 - (IBAction) mySheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode
522                            contextInfo:(void *)contextInfo {
523         
524         [sheet orderOut:self];
525         if(returnCode == 0)  return;
526         // continue with application  
529 - (IBAction)closePrefsSheet:(id)sender
531         //do changes to prefs here
532         //    if (connected) {
533         [tfusbc setDebug:[debugCB state]]; 
534         //  }
535         NSLog(@"auto: %i", [autoCB state]);
536         [prefs setBool:[autoCB state] forKey:@"auto-connect"];
537 //      [prefs setBool:[epgCB state] forKey:@"epgSync"];
538         [prefs synchronize];
539         [NSApp endSheet:prefsWindow returnCode:1];
542 - (IBAction)openRenameSheet:(id)sender
544         if ( [tableView numberOfSelectedRows] != 1 )
545         return;
546         [renameOld setStringValue:[[[dh displayedList] objectAtIndex:[tableView selectedRow]] objectForKey:@"name"]];
547         [renameNew setStringValue:[renameOld stringValue]];
548 //      [renameWindow setTitleBarHeight:0.0];
549         [NSApp beginSheet: renameWindow
550            modalForWindow: mainWindow
551                 modalDelegate: nil
552            didEndSelector: nil
553                   contextInfo: nil];
554     [NSApp runModalForWindow: renameWindow];
555     // Sheet is up here.
558 - (IBAction)cancelRename:(id)sender{
559         [NSApp endSheet: renameWindow];
560     [renameWindow orderOut: self];
561         [NSApp stopModal];
564 - (IBAction)closeRenameSheet:(id)sender
566         //do changes to rename here
567         if (connected) {
568                 [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:[renameOld stringValue],@"oldName",[renameNew stringValue],@"newName", currentPath,@"path",@"rename",@"transferType",nil]];
569                 [self goToPath:currentPath];
570         }
571                 //then close up
572         [self cancelRename:sender];
576 - (IBAction)openNewFolderSheet:(id)sender
578         [NSApp beginSheet: newFolderWindow
579            modalForWindow: mainWindow
580                 modalDelegate: nil
581            didEndSelector: nil
582                   contextInfo: nil];
583     [NSApp runModalForWindow: newFolderWindow];
584     // Sheet is up here.
585         
588 - (IBAction)cancelNewFolder:(id)sender{
589         [NSApp endSheet: newFolderWindow];
590     [newFolderWindow orderOut: self];
591         [NSApp stopModal];
594 - (IBAction)closeNewFolderSheet:(id)sender
596         //do changes to make new folder here
597         if (connected) {
598                 [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:[newFolderNew stringValue],@"newName",currentPath,@"path",@"newFolder",@"transferType", nil]];
599                 [self goToPath:currentPath];
600         }
601         //then close up
602         [self cancelNewFolder:sender];
605 - (IBAction)pathBarClick:(id)sender {
606         int segCount = [pathBar segmentCount]; //remember count starts at 1, not zero!
607         int selectedSegment = [sender selectedSegment]; 
608         if (selectedSegment == segCount - 1) return; //no need to move anywhere
609         int i = 1; // remember have "path" there too 
610         NSMutableArray* pathArray = [NSMutableArray arrayWithCapacity:selectedSegment];
611         while (i<=selectedSegment) {
612                 [pathArray addObject:[[pathBar cell] labelForSegment:i]];
613                 i++;
614         } 
615         NSString* pathString = [pathArray componentsJoinedByString:@"\\"];
616         if ([pathString hasPrefix:@"\\\\"]) pathString = [pathString substringFromIndex:1];
617         [self goToPath:pathString];
618         [self setCurrentPath:pathString];
619         NSRect r = [pathBar frame];
620         [[pathBar cell] setSegmentCount:selectedSegment+1];
621         [pathBar displayIfNeededInRect:r];      
624 - (id)getTfusbc {
625         return tfusbc;
628 - (USBDeviceContext*) getContext {
629         return context;
632 - (NSNumber*) isConnected {
633         return [NSNumber numberWithBool:connected];
637 - (NSString*) currentPath {
638         return currentPath;
641 - (IBAction) toggleTurbo:(id)sender {
642         if ([tfusbc hasCurrentTransfer]) { //have a current transfer, lets toggle it
643                 [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:@"turbo",@"transferType",[NSNumber numberWithBool:[turboCB state]],@"turboOn",nil]];
644         }
647 - (IBAction) toggleTurboCB:(id)sender {
648         if (! connected)
649                 return;
650         if ([turboCB state]) { // currently on, want off
651                 [turboCB setState:0];
652         } else {
653                 [turboCB setState:1];
654         }
655         if ([tfusbc hasCurrentTransfer]) { //have a current transfer, lets toggle it
656                         [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:@"turbo",@"transferType",[NSNumber numberWithBool:[turboCB state]],@"turboOn",nil]];
657         }
660 - (IBAction) togglePreview:(id)sender {
661                 [previewDrawer toggle:sender];
664 /*- (IBAction) snapshot:(id) sender {
665         NSArray* snapshotData = [self snapshotOfPath:@"\\"];
666         [dh setSnapShot:snapshotData];
667         // show snapshot window
668         [snapshotWindow makeKeyAndOrderFront:sender];
669         [outlineView reloadData];
672 - (NSArray*) snapshotOfPath:(NSString*)path {
673         
674 NSData* hddFileData = [tfusbc getFileList:context forPath:path];
675 NSData* checkForError = [hddFileData subdataWithRange:(NSRange){4,4}];
676 const UInt32 check_bigendian = EndianU32_NtoB(USB_Fail);
677 const UInt32 check2_bigendian = EndianU32_NtoB(USB_DataHddDir);
678 if ([checkForError isEqualToData:[NSData dataWithBytes:&check_bigendian length:4]]) {
679         [statusField setStringValue:@"Connected - error on last command"];
681 if (! [checkForError isEqualToData:[NSData dataWithBytes:&check2_bigendian length:4]]) {
682         hddFileData = [tfusbc getFileList:context forPath:path]; //try again
683         if ([checkForError isEqualToData:[NSData dataWithBytes:&check_bigendian length:4]]) {
684                 [statusField setStringValue:@"Connected - error on last command"];
685         }
687 hddFileData = [hddFileData subdataWithRange:(NSRange) {8, [hddFileData length]-8}]; // cut off header and cmd 
688 int i;
689 NSMutableArray* paths = [[NSMutableArray alloc] init];
690 for (i=0; i*114 < [hddFileData length]-4; i++) { // 4 is there cause swapping sometimes adds a byte of padding  
691         NSData* temp = [hddFileData subdataWithRange:(NSRange) {i*114,114}];
692         NSMutableDictionary* tfFile = [dh newTFFileFromSwappedHexData:temp];
693         [dh convertRawDataToUseful:tfFile];
694         if (!([[tfFile objectForKey:@"name"] isEqualToString:@".."])) {
695                 if ([[tfFile objectForKey:@"type"] intValue] == 1) { //folder
696                         NSMutableString* temp = [NSMutableString stringWithString:path];
697                         if (![temp isEqualToString:@"\\"])
698                                 [temp appendString:@"\\"];
699                         [temp appendString:[tfFile objectForKey:@"name"]];
700                         [paths addObject:tfFile];
701                         [paths addObject:[self snapshotOfPath:temp]];
702                 } else {
703                         [paths addObject:tfFile];
704                 }
705         }
706         [tfFile release];
708 return paths;
713 /*- (IBAction) epg:(id) sender {
714         XMLToEPG *xmlConv = [[XMLToEPG alloc] init];
715         NSOpenPanel *nsop = [[NSOpenPanel openPanel]retain];
716     NSArray *fileType = [NSArray arrayWithObject:@"xml"];
717     [nsop setAllowsMultipleSelection:NO];
718     [nsop setCanChooseFiles:YES];
719     [nsop setCanChooseDirectories:NO];
720     [nsop runModalForTypes:fileType];
721     NSMutableArray* importedXML = [xmlConv importXML:[nsop URL]];
722         [xmlConv fixData:importedXML];
723         [xmlConv exportFixedXML:importedXML toFile:@"/Volumes/Tyr/nathan/Desktop/TV/Guides/test.tgd"];
728 /*      if ([prefs boolForKey:@"epgSync"]) {
729                         [epgCB setState:1];
730                         XMLToEPG *xmlConv = [[XMLToEPG alloc] init];
731                         NSString* path = @"/Volumes/Tyr/nathan/Desktop/TV/Guides/tv.xml";
732                         NSString* tempFile = @"/Volumes/Tyr/nathan/Desktop/TV/Guides/test.epg";
733                         NSMutableArray* importedXML = [xmlConv importXML:[NSURL fileURLWithPath:path]];
734                         [xmlConv fixData:importedXML];
735                         [xmlConv exportFixedXML:importedXML toFile:tempFile];
736                         if (! connected)
737                                 return;
738                         NSFileManager* fm = [NSFileManager defaultManager];
739                         NSDictionary *fattrs = [fm fileAttributesAtPath:tempFile traverseLink:NO];
740                         if (!fattrs) {
741                                 NSLog(@"Path of file to upload is incorrect!");
742                                 return;
743                         }
744                         NSDictionary* fileAttr = [dh extractAttributes:fattrs];
745                         NSMutableArray* array = [NSMutableArray arrayWithArray:[tempFile componentsSeparatedByString:@"/"]];
746                         NSString* fileName = [NSString stringWithString:[array lastObject]];
747                         NSData* typeFile = [dh getDataFromTFFile:fileAttr withName:fileName];
748                         [tfusbc uploadFile:tempFile ofSize:[fattrs fileSize] fromPath:@"\\Program Files\\TimerKey\\" withAttributes:typeFile toDevice:context];
749                         [self goToPath:currentPath];
750                         [self resetTurbo];
751                 } */
755 /*- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
757         if ([prefs objectForKey:@"NoRollover"] != nil) return;
758         if ([aTableView mouseOverRow] == rowIndex && ([aTableView selectedRow] != rowIndex))
759                         if ([aTableView lockFocusIfCanDraw]) {
760                                 NSRect rowRect = [aTableView rectOfRow:rowIndex];
761                                 NSRect columnRect = [aTableView rectOfColumn:[[aTableView tableColumns] indexOfObject:aTableColumn]];
763                                         if (!highlightImage) {
764                                                 highlightImage = [[NSImage alloc] initWithData:[highlightImageData propertyList]];
765                                                         [highlightImage setDataRetained:YES]; //?
766                                                         [highlightImage setCacheMode:NSImageCacheAlways]; //?
767                                         }
769                                 [highlightImage drawInRect:NSIntersectionRect(rowRect, columnRect) fromRect:NSMakeRect(0,0,1,[highlightImage size].height) operation:NSCompositeSourceOver fraction:0.3];
770                                 [aTableView unlockFocus];
771                         }
775 - (void)dealloc
777   if (highlightImage) [highlightImage release];
778 [super dealloc];
781 - (IBAction)pauseCurrentTransfer:(id)sender {
782         if (!paused) {
783         [pauseButton setImage:[NSImage imageNamed:@"DownloadResume.tif"]];
784         [pauseButton setAlternateImage:[NSImage imageNamed:@"DownloadResumePressed.tif"]];
785         //add priority "halt" to queue naming file
786         id current = [tfusbc currentTransferInfo];
787         [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:@"pause",@"transferType",[current objectForKey:@"filename"],@"filename",nil]];
788         paused = YES;
789         [currentlyField setStringValue:[NSLocalizedString(@"PAUSED", @"Paused: ") stringByAppendingString:[[current objectForKey:@"filename"]objectForKey:@"name"]]];
790         [connectLight setImage:[NSImage imageNamed:@"green.tiff"]];
791         [currentlyField displayIfNeeded];
792         } else {
793         //change image to pause
794         //add priority "resume" to queue naming file
795         [pauseButton setImage:[NSImage imageNamed:@"DownloadStop.tif"]];
796         [pauseButton setAlternateImage:[NSImage imageNamed:@"DownloadStopPressed.tif"]];
797         id current = [tfusbc firstPausedTransferInfo];//temp as for now only have 1 paused
798         [tfusbc addPriorityTransfer:[NSDictionary dictionaryWithObjectsAndKeys:@"resume",@"transferType",[current objectForKey:@"filename"],@"filename",nil]];
799         paused = NO;
800         [currentlyField setStringValue:[NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:[[current objectForKey:@"filename"]objectForKey:@"name"]]];
801         [connectLight setImage:[NSImage imageNamed:@"blink.tiff"]];
802         [currentlyField displayIfNeeded];
803         }
806 - (id) selectedColumn {
807         return selectedColumn;
809 - (id) currentlyField {
810         return currentlyField;
812 - (id) connectLight {
813         return connectLight;
816 @end
818 @implementation UIElements (PrivateMethods)
820 // To ensure proper maintenance of reference counts, all updates of currentPath
821 // go via this method.
822 - (void) setCurrentPath:(NSString*)path {
823         // Retain first, in case it's the same or a related object.
824         [path retain];
825         [currentPath release];
826         currentPath = path;
829 @end