source: branches/file_selection/macosx/Torrent.m @ 2010

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

getting closer to working file selection gui...now it's a bit more dynamic and accurate, might be a bit slower in theory, though

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