source: trunk/macosx/Torrent.m @ 3192

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

fix progress bar inversion

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