source: trunk/macosx/Torrent.m @ 3218

Last change on this file since 3218 was 3218, checked in by livings124, 15 years ago

remove an unused dictionary

  • Property svn:keywords set to Date Rev Author Id
File size: 53.1 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 3218 2007-09-28 14:31:20Z livings124 $
3 *
4 * Copyright (c) 2006-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "Torrent.h"
26#import "NSStringAdditions.h"
27
28static int static_lastid = 0;
29
30@interface Torrent (Private)
31
32- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle *) lib
33        publicTorrent: (NSNumber *) publicTorrent
34        downloadFolder: (NSString *) downloadFolder
35        useIncompleteFolder: (NSNumber *) useIncompleteFolder incompleteFolder: (NSString *) incompleteFolder
36        dateAdded: (NSDate *) dateAdded dateCompleted: (NSDate *) dateCompleted
37        dateActivity: (NSDate *) dateActivity
38        ratioSetting: (NSNumber *) ratioSetting ratioLimit: (NSNumber *) ratioLimit
39                pex: (NSNumber *) pex
40        waitToStart: (NSNumber *) waitToStart orderValue: (NSNumber *) orderValue;
41
42- (BOOL) shouldUseIncompleteFolderForName: (NSString *) name;
43- (void) updateDownloadFolder;
44
45- (void) createFileList;
46- (void) insertPath: (NSMutableArray *) components forSiblings: (NSMutableArray *) siblings withParent: (NSMutableDictionary *) parent
47            previousPath: (NSString *) previousPath fileSize: (uint64_t) size index: (int) index;
48
49- (void) quickPause;
50- (void) endQuickPause;
51
52- (void) trashFile: (NSString *) path;
53
54@end
55
56@implementation Torrent
57
58- (id) initWithPath: (NSString *) path location: (NSString *) location deleteTorrentFile: (torrentFileState) torrentDelete
59        lib: (tr_handle *) lib
60{
61    self = [self initWithHash: nil path: path lib: lib
62            publicTorrent: torrentDelete != TORRENT_FILE_DEFAULT
63                            ? [NSNumber numberWithBool: torrentDelete == TORRENT_FILE_SAVE] : nil
64            downloadFolder: location
65            useIncompleteFolder: nil incompleteFolder: nil
66            dateAdded: nil dateCompleted: nil
67            dateActivity: nil
68            ratioSetting: nil ratioLimit: nil
69                        pex: nil
70            waitToStart: nil orderValue: nil];
71   
72    if (self)
73    {
74        if (!fPublicTorrent)
75            [self trashFile: path];
76    }
77    return self;
78}
79
80- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle *) lib
81{
82    self = [self initWithHash: [history objectForKey: @"TorrentHash"]
83                path: [history objectForKey: @"TorrentPath"] lib: lib
84                publicTorrent: [history objectForKey: @"PublicCopy"]
85                downloadFolder: [history objectForKey: @"DownloadFolder"]
86                useIncompleteFolder: [history objectForKey: @"UseIncompleteFolder"]
87                incompleteFolder: [history objectForKey: @"IncompleteFolder"]
88                dateAdded: [history objectForKey: @"Date"]
89                                dateCompleted: [history objectForKey: @"DateCompleted"]
90                dateActivity: [history objectForKey: @"DateActivity"]
91                ratioSetting: [history objectForKey: @"RatioSetting"]
92                ratioLimit: [history objectForKey: @"RatioLimit"]
93                                pex: [history objectForKey: @"Pex"]
94                waitToStart: [history objectForKey: @"WaitToStart"]
95                orderValue: [history objectForKey: @"OrderValue"]];
96   
97    if (self)
98    {
99        //start transfer
100        NSNumber * active;
101        if ((active = [history objectForKey: @"Active"]) && [active boolValue])
102        {
103            fStat = tr_torrentStat(fHandle);
104            [self startTransfer];
105        }
106    }
107    return self;
108}
109
110- (NSDictionary *) history
111{
112    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
113                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
114                    [self hashString], @"TorrentHash",
115                    fDownloadFolder, @"DownloadFolder",
116                    [NSNumber numberWithBool: fUseIncompleteFolder], @"UseIncompleteFolder",
117                    [NSNumber numberWithBool: [self isActive]], @"Active",
118                    fDateAdded, @"Date",
119                    [NSNumber numberWithInt: fRatioSetting], @"RatioSetting",
120                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit",
121                    [NSNumber numberWithBool: fWaitToStart], @"WaitToStart",
122                    [self orderValue], @"OrderValue", nil];
123   
124    if (fIncompleteFolder)
125        [history setObject: fIncompleteFolder forKey: @"IncompleteFolder"];
126
127    if (fPublicTorrent)
128        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
129       
130        if (![self privateTorrent])
131                [history setObject: [NSNumber numberWithBool: fPex] forKey: @"Pex"];
132       
133        if (fDateCompleted)
134                [history setObject: fDateCompleted forKey: @"DateCompleted"];
135   
136    NSDate * dateActivity = [self dateActivity];
137    if (dateActivity)
138                [history setObject: dateActivity forKey: @"DateActivity"];
139       
140    return history;
141}
142
143- (void) dealloc
144{
145    if (fileStat)
146        tr_torrentFilesFree(fileStat, [self fileCount]);
147   
148    [fDownloadFolder release];
149    [fIncompleteFolder release];
150   
151    [fPublicTorrentLocation release];
152   
153    [fDateAdded release];
154    [fDateCompleted release];
155    [fDateActivity release];
156   
157    [fIcon release];
158   
159    [fFileList release];
160   
161    [fFileMenu release];
162   
163    [fQuickPauseDict release];
164   
165    [super dealloc];
166}
167
168- (void) closeTorrent
169{
170    tr_torrentClose(fHandle);
171}
172
173- (void) closeRemoveTorrent
174{
175    tr_torrentRemoveSaved(fHandle);
176    [self closeTorrent];
177}
178
179- (void) changeIncompleteDownloadFolder: (NSString *) folder
180{
181    fUseIncompleteFolder = folder != nil;
182   
183    [fIncompleteFolder release];
184    fIncompleteFolder = fUseIncompleteFolder ? [folder retain] : nil;
185   
186    [self updateDownloadFolder];
187}
188
189- (void) changeDownloadFolder: (NSString *) folder
190{
191    [fDownloadFolder release];
192    fDownloadFolder = [folder retain];
193   
194    [self updateDownloadFolder];
195}
196
197- (NSString *) downloadFolder
198{
199    return [NSString stringWithUTF8String: tr_torrentGetFolder(fHandle)];
200}
201
202- (void) getAvailability: (int8_t *) tab size: (int) size
203{
204    tr_torrentAvailability(fHandle, tab, size);
205}
206
207- (void) getAmountFinished: (float *) tab size: (int) size
208{
209    tr_torrentAmountFinished(fHandle, tab, size);
210}
211
212- (void) update
213{
214    fStat = tr_torrentStat(fHandle);
215   
216    //notification when downloading finished
217    if (tr_getComplete(fHandle) || tr_getDone(fHandle))
218    {
219        BOOL canMove = YES;
220       
221        //move file from incomplete folder to download folder
222        if (fUseIncompleteFolder && ![[self downloadFolder] isEqualToString: fDownloadFolder]
223            && (canMove = [self alertForMoveFolderAvailable]))
224        {
225            [self quickPause];
226           
227            if ([[NSFileManager defaultManager] movePath: [[self downloadFolder] stringByAppendingPathComponent: [self name]]
228                                    toPath: [fDownloadFolder stringByAppendingPathComponent: [self name]] handler: nil])
229                [self updateDownloadFolder];
230            else
231                canMove = NO;
232           
233            [self endQuickPause];
234        }
235       
236        if (!canMove)
237        {
238            fUseIncompleteFolder = NO;
239           
240            [fDownloadFolder release];
241            fDownloadFolder = fIncompleteFolder;
242            fIncompleteFolder = nil;
243        }
244               
245                [fDateCompleted release];
246                fDateCompleted = [[NSDate alloc] init];
247       
248        fStat = tr_torrentStat(fHandle);
249        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentFinishedDownloading" object: self];
250    }
251    else if (tr_getIncomplete(fHandle))
252        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentRestartedDownloading" object: self];
253    else;
254   
255    //check to stop for ratio
256    float stopRatio;
257    if ([self isSeeding] && (stopRatio = [self actualStopRatio]) != INVALID && [self ratio] >= stopRatio)
258    {
259        [self stopTransfer];
260        fStat = tr_torrentStat(fHandle);
261       
262        fFinishedSeeding = YES;
263       
264        [self setRatioSetting: NSOffState];
265        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStoppedForRatio" object: self];
266    }
267
268    BOOL wasChecking = fChecking;
269    fChecking = fStat->status == TR_STATUS_CHECK || fStat->status == TR_STATUS_CHECK_WAIT;
270   
271    //check for error
272    BOOL wasError = fError;
273    fError = [self isError];
274   
275    //check if stalled
276    BOOL wasStalled = fStalled;
277    fStalled = [self isActive] && [fDefaults boolForKey: @"CheckStalled"]
278                && [fDefaults integerForKey: @"StalledMinutes"] < [self stalledMinutes];
279   
280    //update queue for checking (from downloading to seeding), stalled, or error
281    if ((wasChecking && !fChecking) || (!wasStalled && fStalled) || (!wasError && fError && [self isActive]))
282        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
283}
284
285- (void) startTransfer
286{
287    fWaitToStart = NO;
288    fFinishedSeeding = NO;
289   
290    if (![self isActive] && [self alertForFolderAvailable] && [self alertForRemainingDiskSpace])
291    {
292        tr_torrentStart(fHandle);
293        [self update];
294    }
295}
296
297- (void) stopTransfer
298{
299    fWaitToStart = NO;
300   
301    if ([self isActive])
302    {
303        tr_torrentStop(fHandle);
304        [self update];
305       
306        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
307    }
308}
309
310- (void) sleep
311{
312    if ((fResumeOnWake = [self isActive]))
313        tr_torrentStop(fHandle);
314}
315
316- (void) wakeUp
317{
318    if (fResumeOnWake)
319        tr_torrentStart(fHandle);
320}
321
322- (void) manualAnnounce
323{
324    tr_manualUpdate(fHandle);
325}
326
327- (BOOL) canManualAnnounce
328{
329    return tr_torrentCanManualUpdate(fHandle);
330}
331
332- (void) resetCache
333{
334    tr_torrentRecheck(fHandle);
335    [self update];
336}
337
338- (float) ratio
339{
340    return fStat->ratio;
341}
342
343- (int) ratioSetting
344{
345    return fRatioSetting;
346}
347
348- (void) setRatioSetting: (int) setting
349{
350    fRatioSetting = setting;
351}
352
353- (float) ratioLimit
354{
355    return fRatioLimit;
356}
357
358- (void) setRatioLimit: (float) limit
359{
360    if (limit >= 0)
361        fRatioLimit = limit;
362}
363
364- (float) actualStopRatio
365{
366    if (fRatioSetting == NSOnState)
367        return fRatioLimit;
368    else if (fRatioSetting == NSMixedState && [fDefaults boolForKey: @"RatioCheck"])
369        return [fDefaults floatForKey: @"RatioLimit"];
370    else
371        return INVALID;
372}
373
374- (float) progressStopRatio
375{
376    float stopRatio, ratio;
377    if ((stopRatio = [self actualStopRatio]) == INVALID || (ratio = [self ratio]) >= stopRatio)
378        return 1.0;
379    else if (ratio > 0 && stopRatio > 0)
380        return ratio / stopRatio;
381    else
382        return 0;
383}
384
385- (tr_speedlimit) speedMode: (BOOL) upload
386{
387    return tr_torrentGetSpeedMode(fHandle, upload ? TR_UP : TR_DOWN);
388}
389
390- (void) setSpeedMode: (tr_speedlimit) mode upload: (BOOL) upload
391{
392    tr_torrentSetSpeedMode(fHandle, upload ? TR_UP : TR_DOWN, mode);
393}
394
395- (int) speedLimit: (BOOL) upload
396{
397    return tr_torrentGetSpeedLimit(fHandle, upload ? TR_UP : TR_DOWN);
398}
399
400- (void) setSpeedLimit: (int) limit upload: (BOOL) upload
401{
402    tr_torrentSetSpeedLimit(fHandle, upload ? TR_UP : TR_DOWN, limit);
403}
404
405- (void) setWaitToStart: (BOOL) wait
406{
407    fWaitToStart = wait;
408}
409
410- (BOOL) waitingToStart
411{
412    return fWaitToStart;
413}
414
415- (void) revealData
416{
417    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
418}
419
420- (void) revealPublicTorrent
421{
422    if (fPublicTorrent)
423        [[NSWorkspace sharedWorkspace] selectFile: fPublicTorrentLocation inFileViewerRootedAtPath: nil];
424}
425
426- (void) trashData
427{
428    [self trashFile: [self dataLocation]];
429}
430
431- (void) trashTorrent
432{
433    if (fPublicTorrent)
434        [self trashFile: [self publicTorrentLocation]];
435}
436
437- (void) moveTorrentDataFileTo: (NSString *) folder
438{
439    NSString * oldFolder = [self downloadFolder];
440    if (![oldFolder isEqualToString: folder] || ![fDownloadFolder isEqualToString: folder])
441    {
442        //check if moving inside itself
443        NSArray * oldComponents = [oldFolder pathComponents],
444                * newComponents = [folder pathComponents];
445        int count;
446       
447        if ((count = [oldComponents count]) < [newComponents count]
448                && [[newComponents objectAtIndex: count] isEqualToString: [self name]]
449                && [oldComponents isEqualToArray:
450                        [newComponents objectsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, count)]]])
451        {
452            NSAlert * alert = [[NSAlert alloc] init];
453            [alert setMessageText: NSLocalizedString(@"A folder cannot be moved to inside itself.",
454                                                        "Move inside itself alert -> title")];
455            [alert setInformativeText: [NSString stringWithFormat:
456                            NSLocalizedString(@"The move operation of \"%@\" cannot be done.",
457                                                "Move inside itself alert -> message"), [self name]]];
458            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Move inside itself alert -> button")];
459           
460            [alert runModal];
461            [alert release];
462           
463            return;
464        }
465       
466        [self quickPause];
467       
468        if ([[NSFileManager defaultManager] movePath: [oldFolder stringByAppendingPathComponent: [self name]]
469                            toPath: [folder stringByAppendingPathComponent: [self name]] handler: nil])
470        {
471            //get rid of both incomplete folder and old download folder, even if move failed
472            fUseIncompleteFolder = NO;
473            if (fIncompleteFolder)
474            {
475                [fIncompleteFolder release];
476                fIncompleteFolder = nil;
477            }
478            [self changeDownloadFolder: folder];
479           
480            [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateInfoSettings" object: nil];
481           
482            [self endQuickPause];
483        }
484        else
485        {
486            [self endQuickPause];
487       
488            NSAlert * alert = [[NSAlert alloc] init];
489            [alert setMessageText: NSLocalizedString(@"There was an error moving the data file.", "Move error alert -> title")];
490            [alert setInformativeText: [NSString stringWithFormat:
491                            NSLocalizedString(@"The move operation of \"%@\" cannot be done.",
492                                                "Move error alert -> message"), [self name]]];
493            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Move error alert -> button")];
494           
495            [alert runModal];
496            [alert release];
497        }
498    }
499}
500
501- (void) copyTorrentFileTo: (NSString *) path
502{
503    [[NSFileManager defaultManager] copyPath: [self torrentLocation] toPath: path handler: nil];
504}
505
506- (BOOL) alertForRemainingDiskSpace
507{
508    if ([self allDownloaded] || ![fDefaults boolForKey: @"WarningRemainingSpace"])
509        return YES;
510   
511    NSString * volumeName;
512    if ((volumeName = [[[NSFileManager defaultManager] componentsToDisplayForPath: [self downloadFolder]] objectAtIndex: 0]))
513    {
514        NSDictionary * systemAttributes = [[NSFileManager defaultManager] fileSystemAttributesAtPath: [self downloadFolder]];
515        uint64_t remainingSpace = [[systemAttributes objectForKey: NSFileSystemFreeSize] unsignedLongLongValue];
516       
517        uint64_t existingSize = 0;
518        NSDirectoryEnumerator * enumerator;
519        if ((enumerator = [[NSFileManager defaultManager] enumeratorAtPath:
520                    [[self downloadFolder] stringByAppendingPathComponent: [self name]]]))
521        {
522            NSDictionary * fileAttributes;
523            while ([enumerator nextObject])
524            {
525                fileAttributes = [enumerator fileAttributes];
526                if (![[fileAttributes objectForKey: NSFileType] isEqualTo: NSFileTypeDirectory])
527                    existingSize += [[fileAttributes objectForKey: NSFileSize] unsignedLongLongValue];
528            }
529        }
530       
531        #warning factor in checked files
532        if (remainingSpace + existingSize <= [self size])
533        {
534            NSAlert * alert = [[NSAlert alloc] init];
535            [alert setMessageText: [NSString stringWithFormat:
536                                    NSLocalizedString(@"Not enough remaining disk space to download \"%@\" completely.",
537                                        "Torrent file disk space alert -> title"), [self name]]];
538            [alert setInformativeText: [NSString stringWithFormat: NSLocalizedString(@"The transfer will be paused."
539                                        " Clear up space on %@ or deselect files in the torrent inspector to continue.",
540                                        "Torrent file disk space alert -> message"), volumeName]];
541            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Torrent file disk space alert -> button")];
542            [alert addButtonWithTitle: NSLocalizedString(@"Download Anyway", "Torrent file disk space alert -> button")];
543            [alert addButtonWithTitle: NSLocalizedString(@"Always Download", "Torrent file disk space alert -> button")];
544           
545            int result = [alert runModal];
546            [alert release];
547           
548            if (result == NSAlertThirdButtonReturn)
549                [fDefaults setBool: NO forKey: @"WarningRemainingSpace"];
550           
551            return result != NSAlertFirstButtonReturn;
552        }
553    }
554    return YES;
555}
556
557- (BOOL) alertForFolderAvailable
558{
559    if (access(tr_torrentGetFolder(fHandle), 0))
560    {
561        NSAlert * alert = [[NSAlert alloc] init];
562        [alert setMessageText: [NSString stringWithFormat:
563                                NSLocalizedString(@"The folder for downloading \"%@\" cannot be used.",
564                                    "Folder cannot be used alert -> title"), [self name]]];
565        [alert setInformativeText: [NSString stringWithFormat:
566                        NSLocalizedString(@"\"%@\" cannot be used. The transfer will be paused.",
567                                            "Folder cannot be used alert -> message"), [self downloadFolder]]];
568        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Folder cannot be used alert -> button")];
569        [alert addButtonWithTitle: [NSLocalizedString(@"Choose New Location",
570                                    "Folder cannot be used alert -> location button") stringByAppendingEllipsis]];
571       
572        if ([alert runModal] != NSAlertFirstButtonReturn)
573        {
574            NSOpenPanel * panel = [NSOpenPanel openPanel];
575           
576            [panel setPrompt: NSLocalizedString(@"Select", "Folder cannot be used alert -> prompt")];
577            [panel setAllowsMultipleSelection: NO];
578            [panel setCanChooseFiles: NO];
579            [panel setCanChooseDirectories: YES];
580            [panel setCanCreateDirectories: YES];
581
582            [panel setMessage: [NSString stringWithFormat: NSLocalizedString(@"Select the download folder for \"%@\"",
583                                "Folder cannot be used alert -> select destination folder"), [self name]]];
584           
585            [[NSNotificationCenter defaultCenter] postNotificationName: @"MakeWindowKey" object: nil];
586            [panel beginSheetForDirectory: nil file: nil types: nil modalForWindow: [NSApp keyWindow] modalDelegate: self
587                    didEndSelector: @selector(destinationChoiceClosed:returnCode:contextInfo:) contextInfo: nil];
588        }
589       
590        [alert release];
591       
592        return NO;
593    }
594    return YES;
595}
596
597- (void) destinationChoiceClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) context
598{
599    if (code != NSOKButton)
600        return;
601   
602    NSString * folder = [[openPanel filenames] objectAtIndex: 0];
603    if (fUseIncompleteFolder)
604        [self changeDownloadFolder: folder];
605    else
606        [self changeDownloadFolder: folder];
607   
608    [self startTransfer];
609    [self update];
610   
611    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateInfoSettings" object: nil];
612}
613
614- (BOOL) alertForMoveFolderAvailable
615{
616    if (access([fDownloadFolder UTF8String], 0))
617    {
618        NSAlert * alert = [[NSAlert alloc] init];
619        [alert setMessageText: [NSString stringWithFormat:
620                                NSLocalizedString(@"The folder for moving the completed \"%@\" cannot be used.",
621                                    "Move folder cannot be used alert -> title"), [self name]]];
622        [alert setInformativeText: [NSString stringWithFormat:
623                                NSLocalizedString(@"\"%@\" cannot be used. The file will remain in its current location.",
624                                    "Move folder cannot be used alert -> message"), fDownloadFolder]];
625        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Move folder cannot be used alert -> button")];
626       
627        [alert runModal];
628        [alert release];
629       
630        return NO;
631    }
632   
633    return YES;
634}
635
636- (NSImage *) icon
637{
638    if (!fIcon)
639    {
640        fIcon = [[[NSWorkspace sharedWorkspace] iconForFileType: fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr')
641                                                : [[self name] pathExtension]] retain];
642        [fIcon setFlipped: YES];
643    }
644    return fIcon;
645}
646
647- (NSString *) name
648{
649    return [NSString stringWithUTF8String: fInfo->name];
650}
651
652- (uint64_t) size
653{
654    return fInfo->totalSize;
655}
656
657- (NSString *) trackerAddress
658{
659    return [NSString stringWithFormat: @"http://%s:%d", fStat->tracker->address, fStat->tracker->port];
660}
661
662- (NSString *) trackerAddressAnnounce
663{
664    return [NSString stringWithUTF8String: fStat->tracker->announce];
665}
666
667- (NSString *) comment
668{
669    return [NSString stringWithUTF8String: fInfo->comment];
670}
671
672- (NSString *) creator
673{
674    return [NSString stringWithUTF8String: fInfo->creator];
675}
676
677- (NSDate *) dateCreated
678{
679    int date = fInfo->dateCreated;
680    return date > 0 ? [NSDate dateWithTimeIntervalSince1970: date] : nil;
681}
682
683- (int) pieceSize
684{
685    return fInfo->pieceSize;
686}
687
688- (int) pieceCount
689{
690    return fInfo->pieceCount;
691}
692
693- (NSString *) hashString
694{
695    return [NSString stringWithUTF8String: fInfo->hashString];
696}
697
698- (BOOL) privateTorrent
699{
700    return TR_FLAG_PRIVATE & fInfo->flags;
701}
702
703- (NSString *) torrentLocation
704{
705    return [NSString stringWithUTF8String: fInfo->torrent];
706}
707
708- (NSString *) publicTorrentLocation
709{
710    return fPublicTorrentLocation;
711}
712
713- (NSString *) dataLocation
714{
715    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
716}
717
718- (BOOL) publicTorrent
719{
720    return fPublicTorrent;
721}
722
723- (float) progress
724{
725    return fStat->percentComplete;
726}
727
728- (float) progressDone
729{
730    return fStat->percentDone;
731}
732
733- (float) progressLeft
734{//NSLog(@"left %f",(float)fStat->leftUntilDone / [self size]);
735    return (float)fStat->leftUntilDone / [self size];
736}
737
738- (int) eta
739{
740    return fStat->eta;
741}
742
743- (NSString * ) etaString
744{
745    int eta = [self eta];
746    if (eta < 0)
747        return @"";
748   
749    NSString * timeString;
750    if (eta < 60)
751        return [NSString stringWithFormat: NSLocalizedString(@"%d sec", "Torrent -> remaining time"), eta];
752    else if (eta < 3600) //60 * 60
753        return [NSString stringWithFormat: NSLocalizedString(@"%d min %d sec", "Torrent -> remaining time"),
754                                                eta / 60, eta % 60];
755    else if (eta < 86400) //24 * 60 * 60
756        return [NSString stringWithFormat: NSLocalizedString(@"%d hr %d min", "Torrent -> remaining time"),
757                                                eta / 3600, (eta / 60) % 60];
758    else
759    {
760        int days = eta / 86400, hours = (eta / 3600) % 24;
761        if (days > 1)
762            return [NSString stringWithFormat: NSLocalizedString(@"%d days %d hr", "Torrent -> remaining time"), days, hours];
763        else
764            return [NSString stringWithFormat: NSLocalizedString(@"1 day %d hr", "Torrent -> remaining time"), hours];
765    }
766}
767
768- (float) notAvailableDesired
769{//NSLog(@"not available %f", (float)(fStat->desiredSize - fStat->desiredAvailable) / [self size]);
770    return (float)(fStat->desiredSize - fStat->desiredAvailable) / [self size];
771}
772
773- (BOOL) isActive
774{
775    return fStat->status & TR_STATUS_ACTIVE;
776}
777
778- (BOOL) isSeeding
779{
780    return fStat->status == TR_STATUS_SEED || fStat->status == TR_STATUS_DONE;
781}
782
783- (BOOL) isPaused
784{
785    return fStat->status == TR_STATUS_STOPPED;
786}
787
788- (BOOL) isChecking
789{
790    return fStat->status == TR_STATUS_CHECK || fStat->status == TR_STATUS_CHECK_WAIT;
791}
792
793- (BOOL) allDownloaded
794{
795    return fStat->cpStatus != TR_CP_INCOMPLETE;
796}
797
798- (BOOL) isComplete
799{
800    return fStat->cpStatus == TR_CP_COMPLETE;
801}
802
803- (BOOL) isError
804{
805    return fStat->error != 0;
806}
807
808- (NSString *) errorMessage
809{
810    if (![self isError])
811        return @"";
812   
813    NSString * error;
814    if (!(error = [NSString stringWithUTF8String: fStat->errorString])
815        && !(error = [NSString stringWithCString: fStat->errorString encoding: NSISOLatin1StringEncoding]))
816        error = NSLocalizedString(@"(unreadable error)", "Torrent -> error string unreadable");
817   
818    return error;
819}
820
821- (NSArray *) peers
822{
823    int totalPeers, i;
824    tr_peer_stat * peers = tr_torrentPeers(fHandle, &totalPeers);
825   
826    NSMutableArray * peerDics = [NSMutableArray arrayWithCapacity: totalPeers];
827    NSMutableDictionary * dic;
828   
829    tr_peer_stat * peer;
830    for (i = 0; i < totalPeers; i++)
831    {
832        peer = &peers[i];
833       
834        dic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
835            [NSNumber numberWithBool: peer->isConnected], @"Connected",
836            [NSNumber numberWithInt: peer->from], @"From",
837            [NSString stringWithCString: (char *) peer->addr encoding: NSUTF8StringEncoding], @"IP",
838            [NSNumber numberWithInt: peer->port], @"Port", nil];
839       
840        if (peer->isConnected)
841        {
842            [dic setObject: [NSNumber numberWithFloat: peer->progress] forKey: @"Progress"];
843           
844            if (peer->isDownloading)
845                [dic setObject: [NSNumber numberWithFloat: peer->uploadToRate] forKey: @"UL To Rate"];
846            if (peer->isUploading)
847                [dic setObject: [NSNumber numberWithFloat: peer->downloadFromRate] forKey: @"DL From Rate"];
848           
849            [dic setObject: [NSNumber numberWithBool: peer->isEncrypted] forKey: @"Encryption"];
850           
851            [dic setObject: [NSString stringWithCString: (char *)peer->client encoding: NSUTF8StringEncoding] forKey: @"Client"];
852        }
853        else
854            [dic setObject: @"" forKey: @"Client"]; //needed to be set here for client sort
855       
856        [peerDics addObject: dic];
857    }
858   
859    tr_torrentPeersFree(peers, totalPeers);
860   
861    return peerDics;
862}
863
864- (NSString *) progressString
865{
866    NSString * string;
867   
868    if (![self allDownloaded])
869    {
870        if ([fDefaults boolForKey: @"DisplayStatusProgressSelected"])
871        {
872            uint64_t have = [self haveTotal];
873            string = [NSString stringWithFormat: NSLocalizedString(@"%@ of %@ selected (%.2f%%)", "Torrent -> progress string"),
874                            [NSString stringForFileSize: have], [NSString stringForFileSize: have + fStat->leftUntilDone],
875                            100.0 * [self progressDone]];
876        }
877        else
878            string = [NSString stringWithFormat: NSLocalizedString(@"%@ of %@ (%.2f%%)", "Torrent -> progress string"),
879                            [NSString stringForFileSize: [self haveTotal]],
880                            [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
881    }
882    else if (![self isComplete])
883    {
884        if ([fDefaults boolForKey: @"DisplayStatusProgressSelected"])
885            string = [NSString stringWithFormat: NSLocalizedString(@"%@ selected, uploaded %@ (Ratio: %@)",
886                "Torrent -> progress string"), [NSString stringForFileSize: [self haveTotal]],
887                [NSString stringForFileSize: [self uploadedTotal]], [NSString stringForRatio: [self ratio]]];
888        else
889            string = [NSString stringWithFormat: NSLocalizedString(@"%@ of %@ (%.2f%%), uploaded %@ (Ratio: %@)",
890                "Torrent -> progress string"), [NSString stringForFileSize: [self haveTotal]],
891                [NSString stringForFileSize: [self size]], 100.0 * [self progress],
892                [NSString stringForFileSize: [self uploadedTotal]], [NSString stringForRatio: [self ratio]]];
893    }
894    else
895        string = [NSString stringWithFormat: NSLocalizedString(@"%@, uploaded %@ (Ratio: %@)", "Torrent -> progress string"),
896                [NSString stringForFileSize: [self size]], [NSString stringForFileSize: [self uploadedTotal]],
897                [NSString stringForRatio: [self ratio]]];
898   
899    //add time when downloading
900    if (fStat->status == TR_STATUS_DOWNLOAD)
901    {
902        string = [self eta] >= 0
903            ? [string stringByAppendingFormat: NSLocalizedString(@" - %@ remaining", "Torrent -> progress string"), [self etaString]]
904            : [string stringByAppendingString: NSLocalizedString(@" - remaining time unknown", "Torrent -> progress string")];
905    }
906   
907    return string;
908}
909
910- (NSString *) statusString
911{
912    NSString * string;
913   
914    if ([self isError])
915    {
916        NSString * errorString = [self errorMessage];
917        if (!errorString || [errorString isEqualToString: @""])
918            string = NSLocalizedString(@"Error", "Torrent -> status string");
919        else
920            string = [NSLocalizedString(@"Error: ", "Torrent -> status string") stringByAppendingString: errorString];
921    }
922    else
923    {
924        switch (fStat->status)
925        {
926            case TR_STATUS_STOPPED:
927                if (fWaitToStart)
928                {
929                    string = ![self allDownloaded]
930                            ? [NSLocalizedString(@"Waiting to download", "Torrent -> status string") stringByAppendingEllipsis]
931                            : [NSLocalizedString(@"Waiting to seed", "Torrent -> status string") stringByAppendingEllipsis];
932                }
933                else if (fFinishedSeeding)
934                    string = NSLocalizedString(@"Seeding complete", "Torrent -> status string");
935                else
936                    string = NSLocalizedString(@"Paused", "Torrent -> status string");
937                break;
938
939            case TR_STATUS_CHECK_WAIT:
940                string = [NSLocalizedString(@"Waiting to check existing data", "Torrent -> status string") stringByAppendingEllipsis];
941                break;
942
943            case TR_STATUS_CHECK:
944                string = [NSString stringWithFormat: NSLocalizedString(@"Checking existing data (%.2f%%)",
945                                        "Torrent -> status string"), 100.0 * fStat->recheckProgress];
946                break;
947
948            case TR_STATUS_DOWNLOAD:
949                if ([self totalPeersConnected] != 1)
950                    string = [NSString stringWithFormat: NSLocalizedString(@"Downloading from %d of %d peers",
951                                                    "Torrent -> status string"), [self peersSendingToUs], [self totalPeersConnected]];
952                else
953                    string = [NSString stringWithFormat: NSLocalizedString(@"Downloading from %d of 1 peer",
954                                                    "Torrent -> status string"), [self peersSendingToUs]];
955               
956                string = [string stringByAppendingFormat: @" - DL: %@, UL: %@",
957                            [NSString stringForSpeed: [self downloadRate]], [NSString stringForSpeed: [self uploadRate]]];
958                break;
959
960            case TR_STATUS_SEED:
961            case TR_STATUS_DONE:
962                if ([self totalPeersConnected] != 1)
963                    string = [NSString stringWithFormat: NSLocalizedString(@"Seeding to %d of %d peers", "Torrent -> status string"),
964                                                    [self peersGettingFromUs], [self totalPeersConnected]];
965                else
966                    string = [NSString stringWithFormat: NSLocalizedString(@"Seeding to %d of 1 peer", "Torrent -> status string"),
967                                                    [self peersGettingFromUs]];
968               
969                string = [string stringByAppendingFormat: @" - UL: %@", [NSString stringForSpeed: [self uploadRate]]];
970                break;
971
972            case TR_STATUS_STOPPING:
973                string = [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
974                break;
975           
976            default:
977                string = @"";
978        }
979       
980        if (fStalled)
981            string = [NSLocalizedString(@"Stalled, ", "Torrent -> status string") stringByAppendingString: string];
982    }
983   
984    return string;
985}
986
987- (NSString *) shortStatusString
988{
989    NSString * string;
990   
991    switch (fStat->status)
992    {
993        case TR_STATUS_STOPPED:
994            if (fWaitToStart)
995            {
996                string = ![self allDownloaded]
997                        ? [NSLocalizedString(@"Waiting to download", "Torrent -> status string") stringByAppendingEllipsis]
998                        : [NSLocalizedString(@"Waiting to seed", "Torrent -> status string") stringByAppendingEllipsis];
999            }
1000            else if (fFinishedSeeding)
1001                string = NSLocalizedString(@"Seeding complete", "Torrent -> status string");
1002            else
1003                string = NSLocalizedString(@"Paused", "Torrent -> status string");
1004            break;
1005
1006        case TR_STATUS_CHECK_WAIT:
1007            string = [NSLocalizedString(@"Waiting to check existing data", "Torrent -> status string") stringByAppendingEllipsis];
1008            break;
1009
1010        case TR_STATUS_CHECK:
1011            string = [NSString stringWithFormat: NSLocalizedString(@"Checking existing data (%.2f%%)",
1012                                    "Torrent -> status string"), 100.0 * fStat->recheckProgress];
1013            break;
1014
1015        case TR_STATUS_STOPPING:
1016            string = [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
1017            break;
1018       
1019        default:
1020            if ([self isActive])
1021            {
1022                if (![self allDownloaded])
1023                    string = [NSString stringWithFormat: NSLocalizedString(@"DL: %@, ", "Torrent -> status string"),
1024                                        [NSString stringForSpeed: [self downloadRate]]];
1025                else
1026                    string = [NSString stringWithFormat: NSLocalizedString(@"Ratio: %@, ", "Torrent -> status string"),
1027                                [NSString stringForRatio: [self ratio]]];
1028               
1029                string = [string stringByAppendingFormat: NSLocalizedString(@"UL: %@", "Torrent -> status string"),
1030                                [NSString stringForSpeed: [self uploadRate]]];
1031            }
1032            else
1033                string = @"";
1034    }
1035   
1036    return string;
1037}
1038
1039- (NSString *) remainingTimeString
1040{
1041    switch (fStat->status)
1042    {
1043        case TR_STATUS_CHECK_WAIT:
1044            return [NSLocalizedString(@"Waiting to check existing data", "Torrent -> status string") stringByAppendingEllipsis];
1045            break;
1046
1047        case TR_STATUS_CHECK:
1048            return [NSString stringWithFormat: NSLocalizedString(@"Checking existing data (%.2f%%)",
1049                                    "Torrent -> status string"), 100.0 * fStat->recheckProgress];
1050            break;
1051
1052        case TR_STATUS_DOWNLOAD:
1053            return [self eta] >= 0 ? [self etaString] : NSLocalizedString(@"Unknown", "Torrent -> remaining time");
1054            break;
1055       
1056        case TR_STATUS_SEED:
1057        case TR_STATUS_DONE:
1058            return [NSLocalizedString(@"Ratio: ", "Torrent -> status string") stringByAppendingString:
1059                                                                            [NSString stringForRatio: [self ratio]]];
1060            break;
1061       
1062        default:
1063            return @"";
1064    }
1065}
1066
1067- (NSString *) stateString
1068{
1069    switch (fStat->status)
1070    {
1071        case TR_STATUS_STOPPED:
1072            return NSLocalizedString(@"Paused", "Torrent -> status string");
1073            break;
1074
1075        case TR_STATUS_CHECK:
1076            return [NSString stringWithFormat: NSLocalizedString(@"Checking existing data (%.2f%%)",
1077                                    "Torrent -> status string"), 100.0 * fStat->recheckProgress];
1078            break;
1079       
1080        case TR_STATUS_CHECK_WAIT:
1081            return [NSLocalizedString(@"Waiting to check existing data", "Torrent -> status string") stringByAppendingEllipsis];
1082            break;
1083
1084        case TR_STATUS_DOWNLOAD:
1085            return NSLocalizedString(@"Downloading", "Torrent -> status string");
1086            break;
1087
1088        case TR_STATUS_SEED:
1089        case TR_STATUS_DONE:
1090            return NSLocalizedString(@"Seeding", "Torrent -> status string");
1091            break;
1092
1093        case TR_STATUS_STOPPING:
1094            return [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
1095            break;
1096       
1097        default:
1098            return NSLocalizedString(@"N/A", "Torrent -> status string");
1099    }
1100}
1101
1102- (int) seeders
1103{
1104    return fStat->seeders;
1105}
1106
1107- (int) leechers
1108{
1109    return fStat->leechers;
1110}
1111
1112- (int) completedFromTracker
1113{
1114    return fStat->completedFromTracker;
1115}
1116
1117- (int) totalPeersConnected
1118{
1119    return fStat->peersConnected;
1120}
1121
1122- (int) totalPeersTracker
1123{
1124    return fStat->peersFrom[TR_PEER_FROM_TRACKER];
1125}
1126
1127- (int) totalPeersIncoming
1128{
1129    return fStat->peersFrom[TR_PEER_FROM_INCOMING];
1130}
1131
1132- (int) totalPeersCache
1133{
1134    return fStat->peersFrom[TR_PEER_FROM_CACHE];
1135}
1136
1137- (int) totalPeersPex
1138{
1139    return fStat->peersFrom[TR_PEER_FROM_PEX];
1140}
1141
1142- (int) peersSendingToUs
1143{
1144    return fStat->peersSendingToUs;
1145}
1146
1147- (int) peersGettingFromUs
1148{
1149    return fStat->peersGettingFromUs;
1150}
1151
1152- (float) downloadRate
1153{
1154    return fStat->rateDownload;
1155}
1156
1157- (float) uploadRate
1158{
1159    return fStat->rateUpload;
1160}
1161
1162- (uint64_t) haveVerified
1163{
1164    return fStat->haveValid;
1165}
1166
1167- (uint64_t) haveTotal
1168{
1169    return [self haveVerified] + fStat->haveUnchecked;
1170}
1171
1172- (uint64_t) downloadedTotal
1173{
1174    return fStat->downloadedEver;
1175}
1176
1177- (uint64_t) uploadedTotal
1178{
1179    return fStat->uploadedEver;
1180}
1181
1182- (uint64_t) failedHash
1183{
1184    return fStat->corruptEver;
1185}
1186
1187- (float) swarmSpeed
1188{
1189    return fStat->swarmspeed;
1190}
1191
1192- (BOOL) pex
1193{
1194        return fPex;
1195}
1196
1197- (void) setPex: (BOOL) setting
1198{
1199        if (![self privateTorrent])
1200        {
1201                fPex = setting;
1202                tr_torrentDisablePex(fHandle, !setting);
1203        }
1204}
1205
1206- (NSNumber *) orderValue
1207{
1208    return [NSNumber numberWithInt: fOrderValue];
1209}
1210
1211- (void) setOrderValue: (int) orderValue
1212{
1213    fOrderValue = orderValue;
1214}
1215
1216- (NSArray *) fileList
1217{
1218    return fFileList;
1219}
1220
1221- (int) fileCount
1222{
1223    return fInfo->fileCount;
1224}
1225
1226- (void) updateFileStat
1227{
1228    if (fileStat)
1229        tr_torrentFilesFree(fileStat, [self fileCount]);
1230   
1231    int count;
1232    fileStat = tr_torrentFiles(fHandle, &count);
1233}
1234
1235- (float) fileProgress: (int) index
1236{
1237    if (!fileStat)
1238        [self updateFileStat];
1239       
1240    return fileStat[index].progress;
1241}
1242
1243- (BOOL) canChangeDownloadCheckForFile: (int) index
1244{
1245    if (!fileStat)
1246        [self updateFileStat];
1247   
1248    return [self fileCount] > 1 && fileStat[index].completionStatus != TR_CP_COMPLETE;
1249}
1250
1251- (BOOL) canChangeDownloadCheckForFiles: (NSIndexSet *) indexSet
1252{
1253    if ([self fileCount] <= 1 || [self isComplete])
1254        return NO;
1255   
1256    if (!fileStat)
1257        [self updateFileStat];
1258   
1259    int index;
1260    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1261        if (fileStat[index].completionStatus != TR_CP_COMPLETE)
1262            return YES;
1263    return NO;
1264}
1265
1266- (int) checkForFiles: (NSIndexSet *) indexSet
1267{
1268    BOOL onState = NO, offState = NO;
1269    int index;
1270    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1271    {
1272        if (tr_torrentGetFileDL(fHandle, index) || ![self canChangeDownloadCheckForFile: index])
1273            onState = YES;
1274        else
1275            offState = YES;
1276       
1277        if (onState && offState)
1278            return NSMixedState;
1279    }
1280    return onState ? NSOnState : NSOffState;
1281}
1282
1283- (void) setFileCheckState: (int) state forIndexes: (NSIndexSet *) indexSet
1284{
1285    int count = [indexSet count], i = 0, index;
1286    int * files = malloc(count * sizeof(int));
1287    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1288    {
1289        files[i] = index;
1290        i++;
1291    }
1292   
1293    tr_torrentSetFileDLs(fHandle, files, count, state != NSOffState);
1294    free(files);
1295   
1296    [self update];
1297}
1298
1299- (void) setFilePriority: (int) priority forIndexes: (NSIndexSet *) indexSet
1300{
1301    int count = [indexSet count], i = 0, index;
1302    int * files = malloc(count * sizeof(int));
1303    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1304    {
1305        files[i] = index;
1306        i++;
1307    }
1308
1309    tr_torrentSetFilePriorities(fHandle, files, count, priority);
1310    free(files);
1311}
1312
1313- (BOOL) hasFilePriority: (int) priority forIndexes: (NSIndexSet *) indexSet
1314{
1315    int index;
1316    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1317        if (priority == tr_torrentGetFilePriority(fHandle, index) && [self canChangeDownloadCheckForFile: index])
1318            return YES;
1319    return NO;
1320}
1321
1322- (NSSet *) filePrioritiesForIndexes: (NSIndexSet *) indexSet
1323{
1324    BOOL low = NO, normal = NO, high = NO;
1325    NSMutableSet * priorities = [NSMutableSet setWithCapacity: 3];
1326   
1327    int index, priority;
1328    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1329    {
1330        if (![self canChangeDownloadCheckForFile: index])
1331            continue;
1332       
1333        priority = tr_torrentGetFilePriority(fHandle, index);
1334        if (priority == TR_PRI_LOW)
1335        {
1336            if (low)
1337                continue;
1338            low = YES;
1339        }
1340        else if (priority == TR_PRI_HIGH)
1341        {
1342            if (high)
1343                continue;
1344            high = YES;
1345        }
1346        else
1347        {
1348            if (normal)
1349                continue;
1350            normal = YES;
1351        }
1352       
1353        [priorities addObject: [NSNumber numberWithInt: priority]];
1354        if (low && normal && high)
1355            break;
1356    }
1357    return priorities;
1358}
1359
1360- (NSMenu *) fileMenu
1361{
1362    if (!fFileMenu)
1363    {
1364        fFileMenu = [[NSMenu alloc] initWithTitle: [@"TorrentMenu:" stringByAppendingString: [self name]]];
1365        [fFileMenu setAutoenablesItems: NO];
1366    }
1367    return fFileMenu;
1368}
1369
1370- (NSDate *) dateAdded
1371{
1372    return fDateAdded;
1373}
1374
1375- (NSDate *) dateCompleted
1376{
1377    return fDateCompleted;
1378}
1379
1380- (NSDate *) dateActivity
1381{
1382    uint64_t date = fStat->activityDate;
1383    return date != 0 ? [NSDate dateWithTimeIntervalSince1970: date / 1000] : fDateActivity;
1384}
1385
1386- (int) stalledMinutes
1387{
1388    uint64_t start;
1389    if ((start = fStat->startDate) == 0)
1390        return -1;
1391   
1392    NSDate * started = [NSDate dateWithTimeIntervalSince1970: start / 1000],
1393            * activity = [self dateActivity];
1394   
1395    NSDate * laterDate = (!activity || [started compare: activity] == NSOrderedDescending) ? started : activity;
1396    return -1 * [laterDate timeIntervalSinceNow] / 60;
1397}
1398
1399- (BOOL) isStalled
1400{
1401    return fStalled;
1402}
1403
1404- (NSNumber *) stateSortKey
1405{
1406    if (![self isActive])
1407        return [NSNumber numberWithInt: 0];
1408    else if ([self isSeeding])
1409        return [NSNumber numberWithInt: 1];
1410    else
1411        return [NSNumber numberWithInt: 2];
1412}
1413
1414- (NSNumber *) progressSortKey
1415{
1416    return [NSNumber numberWithFloat: [self progress]];
1417}
1418
1419- (NSNumber *) ratioSortKey
1420{
1421    return [NSNumber numberWithFloat: [self ratio]];
1422}
1423
1424- (NSNumber *) ratioProgressSortKey
1425{
1426    return [NSNumber numberWithFloat: [self progressStopRatio]];
1427}
1428
1429- (int) torrentID
1430{
1431    return fID;
1432}
1433
1434- (const tr_info *) torrentInfo
1435{
1436    return fInfo;
1437}
1438
1439- (const tr_stat *) torrentStat
1440{
1441    return fStat;
1442}
1443
1444@end
1445
1446@implementation Torrent (Private)
1447
1448//if a hash is given, attempt to load that; otherwise, attempt to open file at path
1449- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle *) lib
1450        publicTorrent: (NSNumber *) publicTorrent
1451        downloadFolder: (NSString *) downloadFolder
1452        useIncompleteFolder: (NSNumber *) useIncompleteFolder incompleteFolder: (NSString *) incompleteFolder
1453        dateAdded: (NSDate *) dateAdded dateCompleted: (NSDate *) dateCompleted
1454        dateActivity: (NSDate *) dateActivity
1455        ratioSetting: (NSNumber *) ratioSetting ratioLimit: (NSNumber *) ratioLimit
1456                pex: (NSNumber *) pex
1457        waitToStart: (NSNumber *) waitToStart orderValue: (NSNumber *) orderValue;
1458{
1459    if (!(self = [super init]))
1460        return nil;
1461   
1462    static_lastid++;
1463    fID = static_lastid;
1464   
1465    fLib = lib;
1466    fDefaults = [NSUserDefaults standardUserDefaults];
1467
1468    fPublicTorrent = path && (publicTorrent ? [publicTorrent boolValue] : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
1469    if (fPublicTorrent)
1470        fPublicTorrentLocation = [path retain];
1471   
1472    fDownloadFolder = downloadFolder ? downloadFolder : [fDefaults stringForKey: @"DownloadFolder"];
1473    fDownloadFolder = [[fDownloadFolder stringByExpandingTildeInPath] retain];
1474   
1475    fUseIncompleteFolder = useIncompleteFolder ? [useIncompleteFolder boolValue]
1476                                : [fDefaults boolForKey: @"UseIncompleteDownloadFolder"];
1477    if (fUseIncompleteFolder)
1478    {
1479        fIncompleteFolder = incompleteFolder ? incompleteFolder : [fDefaults stringForKey: @"IncompleteDownloadFolder"];
1480        fIncompleteFolder = [[fIncompleteFolder stringByExpandingTildeInPath] retain];
1481    }
1482   
1483    NSString * currentDownloadFolder;
1484    tr_info info;
1485    int error;
1486    if (hashString)
1487    {
1488        if (tr_torrentParseHash(fLib, [hashString UTF8String], NULL, &info) == TR_OK)
1489        {
1490            currentDownloadFolder = [self shouldUseIncompleteFolderForName: [NSString stringWithUTF8String: info.name]]
1491                                        ? fIncompleteFolder : fDownloadFolder;
1492            fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], [currentDownloadFolder UTF8String],
1493                                            TR_FLAG_SAVE | TR_FLAG_PAUSED, &error);
1494        }
1495        tr_metainfoFree(&info);
1496    }
1497    if (!fHandle && path)
1498    {
1499        if (tr_torrentParse(fLib, [path UTF8String], NULL, &info) == TR_OK)
1500        {
1501            currentDownloadFolder = [self shouldUseIncompleteFolderForName: [NSString stringWithUTF8String: info.name]]
1502                                        ? fIncompleteFolder : fDownloadFolder;
1503            fHandle = tr_torrentInit(fLib, [path UTF8String], [currentDownloadFolder UTF8String],
1504                                        TR_FLAG_SAVE | TR_FLAG_PAUSED, &error);
1505        }
1506        tr_metainfoFree(&info);
1507    }
1508    if (!fHandle)
1509    {
1510        [self release];
1511        return nil;
1512    }
1513   
1514    fInfo = tr_torrentInfo(fHandle);
1515
1516    fDateAdded = dateAdded ? [dateAdded retain] : [[NSDate alloc] init];
1517        if (dateCompleted)
1518                fDateCompleted = [dateCompleted retain];
1519    if (dateActivity)
1520                fDateActivity = [dateActivity retain];
1521       
1522    fRatioSetting = ratioSetting ? [ratioSetting intValue] : NSMixedState;
1523    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
1524    fFinishedSeeding = NO;
1525       
1526        if ([self privateTorrent])
1527                fPex = NO;
1528        else
1529                fPex = pex ? [pex boolValue] : YES;
1530        tr_torrentDisablePex(fHandle, !fPex);
1531   
1532    fWaitToStart = waitToStart ? [waitToStart boolValue] : [fDefaults boolForKey: @"AutoStartDownload"];
1533    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
1534    fError = NO;
1535   
1536    [self createFileList];
1537   
1538    [self update];
1539    return self;
1540}
1541
1542- (void) createFileList
1543{
1544    int count = [self fileCount], i;
1545    tr_file * file;
1546    NSMutableArray * pathComponents;
1547    NSString * path;
1548   
1549    NSMutableArray * fileList = [[NSMutableArray alloc] initWithCapacity: count],
1550                    * flatFileList = [[NSMutableArray alloc] initWithCapacity: count];
1551   
1552    for (i = 0; i < count; i++)
1553    {
1554        file = &fInfo->files[i];
1555       
1556        pathComponents = [[[NSString stringWithUTF8String: file->name] pathComponents] mutableCopy];
1557        if (fInfo->multifile)
1558        {
1559            path = [pathComponents objectAtIndex: 0];
1560            [pathComponents removeObjectAtIndex: 0];
1561        }
1562        else
1563            path = @"";
1564       
1565        [self insertPath: pathComponents forSiblings: fileList withParent: nil previousPath: path fileSize: file->length index: i];
1566        [pathComponents autorelease];
1567    }
1568   
1569    fFileList = [[NSArray alloc] initWithArray: fileList];
1570    [fileList release];
1571    [flatFileList release];
1572}
1573
1574- (void) insertPath: (NSMutableArray *) components forSiblings: (NSMutableArray *) siblings withParent: (NSMutableDictionary *) parent
1575            previousPath: (NSString *) previousPath fileSize: (uint64_t) size index: (int) index
1576{
1577    NSString * name = [components objectAtIndex: 0];
1578    BOOL isFolder = [components count] > 1;
1579   
1580    NSMutableDictionary * dict = nil;
1581    if (isFolder)
1582    {
1583        NSEnumerator * enumerator = [siblings objectEnumerator];
1584        while ((dict = [enumerator nextObject]))
1585            if ([[dict objectForKey: @"IsFolder"] boolValue] && [[dict objectForKey: @"Name"] isEqualToString: name])
1586                break;
1587    }
1588   
1589    NSString * currentPath = [previousPath stringByAppendingPathComponent: name];
1590   
1591    //create new folder or item if it doesn't already exist
1592    if (!dict)
1593    {
1594        dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: name, @"Name",
1595                [NSNumber numberWithBool: isFolder], @"IsFolder", currentPath, @"Path", nil];
1596        [siblings addObject: dict];
1597       
1598        if (isFolder)
1599        {
1600            [dict setObject: [NSMutableArray array] forKey: @"Children"];
1601            [dict setObject: [NSMutableIndexSet indexSetWithIndex: index] forKey: @"Indexes"];
1602        }
1603        else
1604        {
1605            [dict setObject: [NSIndexSet indexSetWithIndex: index] forKey: @"Indexes"];
1606            [dict setObject: [NSNumber numberWithUnsignedLongLong: size] forKey: @"Size"];
1607           
1608            NSImage * icon = [[NSWorkspace sharedWorkspace] iconForFileType: [name pathExtension]];
1609            [icon setFlipped: YES];
1610            [dict setObject: icon forKey: @"Icon"];
1611        }
1612       
1613        if (parent)
1614            [dict setObject: parent forKey: @"Parent"];
1615    }
1616    else
1617        [[dict objectForKey: @"Indexes"] addIndex: index];
1618   
1619    if (isFolder)
1620    {
1621        [components removeObjectAtIndex: 0];
1622        [self insertPath: components forSiblings: [dict objectForKey: @"Children"] withParent: dict
1623                previousPath: currentPath fileSize: size index: index];
1624    }
1625}
1626
1627- (BOOL) shouldUseIncompleteFolderForName: (NSString *) name
1628{
1629    return fUseIncompleteFolder &&
1630        ![[NSFileManager defaultManager] fileExistsAtPath: [fDownloadFolder stringByAppendingPathComponent: name]];
1631}
1632
1633- (void) updateDownloadFolder
1634{
1635    NSString * folder = [self shouldUseIncompleteFolderForName: [self name]] ? fIncompleteFolder : fDownloadFolder;
1636    tr_torrentSetFolder(fHandle, [folder UTF8String]);
1637}
1638
1639- (void) quickPause
1640{
1641    if (fQuickPauseDict)
1642        return;
1643
1644    fQuickPauseDict = [[NSDictionary alloc] initWithObjectsAndKeys:
1645                    [NSNumber numberWithInt: [self speedMode: YES]], @"UploadSpeedMode",
1646                    [NSNumber numberWithInt: [self speedLimit: YES]], @"UploadSpeedLimit",
1647                    [NSNumber numberWithInt: [self speedMode: NO]], @"DownloadSpeedMode",
1648                    [NSNumber numberWithInt: [self speedLimit: NO]], @"DownloadSpeedLimit", nil];
1649   
1650    [self setSpeedMode: TR_SPEEDLIMIT_SINGLE upload: YES];
1651    [self setSpeedLimit: 0 upload: YES];
1652    [self setSpeedMode: TR_SPEEDLIMIT_SINGLE upload: NO];
1653    [self setSpeedLimit: 0 upload: NO];
1654}
1655
1656- (void) endQuickPause
1657{
1658    if (!fQuickPauseDict)
1659        return;
1660   
1661    [self setSpeedMode: [[fQuickPauseDict objectForKey: @"UploadSpeedMode"] intValue] upload: YES];
1662    [self setSpeedLimit: [[fQuickPauseDict objectForKey: @"UploadSpeedLimit"] intValue] upload: YES];
1663    [self setSpeedMode: [[fQuickPauseDict objectForKey: @"DownloadSpeedMode"] intValue] upload: NO];
1664    [self setSpeedLimit: [[fQuickPauseDict objectForKey: @"DownloadSpeedLimit"] intValue] upload: NO];
1665   
1666    [fQuickPauseDict release];
1667    fQuickPauseDict = nil;
1668}
1669
1670- (void) trashFile: (NSString *) path
1671{
1672    //attempt to move to trash
1673    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
1674            source: [path stringByDeletingLastPathComponent] destination: @""
1675            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
1676    {
1677        //if cannot trash, just delete it (will work if it is on a remote volume)
1678        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
1679            NSLog(@"Could not trash %@", path);
1680    }
1681}
1682
1683@end
Note: See TracBrowser for help on using the repository browser.