source: trunk/macosx/Torrent.m @ 3216

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

combine some progress string code and release an attributes dictionary when it's not needed

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