source: trunk/macosx/Torrent.m @ 3205

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

instead of creating every string, including both minimal and regular together, for every torrent, generate the string when requested

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