source: trunk/macosx/Torrent.m @ 2169

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

simplify some of the repetitive queue code a bit

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