source: trunk/macosx/Torrent.m @ 2450

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

fix some exceptions based on forgetting to raname some methods

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