source: trunk/macosx/Torrent.m @ 1026

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

Use localized strings where necessary. Also a fix for the port checker. Thanks to Cédric Luthi for these.

  • Property svn:keywords set to Date Rev Author Id
File size: 29.8 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 1026 2006-10-24 20:34:13Z livings124 $
3 *
4 * Copyright (c) 2006 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
33@interface Torrent (Private)
34
35- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
36        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
37        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
38        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
39        orderValue: (NSNumber *) orderValue;
40
41- (NSImage *) advancedBar;
42
43- (void) trashFile: (NSString *) path;
44
45@end
46
47@implementation Torrent
48
49// Used to optimize drawing. They contain packed RGBA pixels for every color needed.
50#define BE OSSwapBigToHostConstInt32
51
52static uint32_t kRed   = BE(0xFF6450FF), //255, 100, 80
53                kBlue = BE(0x50A0FFFF), //80, 160, 255
54                kBlue2 = BE(0x1E46B4FF), //30, 70, 180
55                kGray  = BE(0x969696FF), //150, 150, 150
56                kGreen1 = BE(0x99FFCCFF), //153, 255, 204
57                kGreen2 = BE(0x66FF99FF), //102, 255, 153
58                kGreen3 = BE(0x00FF66FF), //0, 255, 102
59                kWhite = BE(0xFFFFFFFF); //255, 255, 255
60
61- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib
62{
63    self = [self initWithHash: nil path: path lib: lib privateTorrent: nil publicTorrent: nil
64            date: nil stopRatioSetting: nil ratioLimit: nil waitToStart: nil orderValue: nil];
65   
66    if (self)
67    {
68        if (!fPublicTorrent)
69            [self trashFile: path];
70    }
71    return self;
72}
73
74- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle_t *) lib
75{
76    self = [self initWithHash: [history objectForKey: @"TorrentHash"]
77                path: [history objectForKey: @"TorrentPath"] lib: lib
78                privateTorrent: [history objectForKey: @"PrivateCopy"]
79                publicTorrent: [history objectForKey: @"PublicCopy"]
80                date: [history objectForKey: @"Date"]
81                stopRatioSetting: [history objectForKey: @"StopRatioSetting"]
82                ratioLimit: [history objectForKey: @"RatioLimit"]
83                waitToStart: [history objectForKey: @"WaitToStart"]
84                orderValue: [history objectForKey: @"OrderValue"]];
85   
86    if (self)
87    {
88        NSString * downloadFolder;
89        if (!(downloadFolder = [history objectForKey: @"DownloadFolder"]))
90            downloadFolder = [[fDefaults stringForKey: @"DownloadFolder"] stringByExpandingTildeInPath];
91        [self setDownloadFolder: downloadFolder];
92
93        NSString * paused;
94        if (!(paused = [history objectForKey: @"Paused"]) || [paused isEqualToString: @"NO"])
95        {
96            fStat = tr_torrentStat(fHandle);
97            [self startTransfer];
98        }
99    }
100    return self;
101}
102
103- (NSDictionary *) history
104{
105    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
106                    [NSNumber numberWithBool: fPrivateTorrent], @"PrivateCopy",
107                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
108                    [self downloadFolder], @"DownloadFolder",
109                    [self isActive] ? @"NO" : @"YES", @"Paused",
110                    [self date], @"Date",
111                    [NSNumber numberWithInt: fStopRatioSetting], @"StopRatioSetting",
112                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit",
113                    [NSNumber numberWithBool: fWaitToStart], @"WaitToStart",
114                    [self orderValue], @"OrderValue", nil];
115           
116    if (fPrivateTorrent)
117        [history setObject: [self hashString] forKey: @"TorrentHash"];
118
119    if (fPublicTorrent)
120        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
121   
122    return history;
123}
124
125- (void) dealloc
126{
127    if (fHandle)
128    {
129        tr_torrentClose(fLib, fHandle);
130       
131        if (fPublicTorrentLocation)
132            [fPublicTorrentLocation release];
133       
134        [fDate release];
135       
136        [fIcon release];
137        [fIconFlipped release];
138        [fIconSmall release];
139       
140        [fProgressString release];
141        [fStatusString release];
142        [fShortStatusString release];
143        [fRemainingTimeString release];
144       
145       
146        [fBitmap release];
147        free(fPieces);
148    }
149    [super dealloc];
150}
151
152- (void) setDownloadFolder: (NSString *) path
153{
154    tr_torrentSetFolder(fHandle, [path UTF8String]);
155}
156
157- (NSString *) downloadFolder
158{
159    return [NSString stringWithUTF8String: tr_torrentGetFolder(fHandle)];
160}
161
162- (void) getAvailability: (int8_t *) tab size: (int) size
163{
164    tr_torrentAvailability(fHandle, tab, size);
165}
166
167- (void) getAmountFinished: (float *) tab size: (int) size
168{
169    tr_torrentAmountFinished(fHandle, tab, size);
170}
171
172- (void) update
173{
174    fStat = tr_torrentStat(fHandle);
175   
176    //notification when downloading finished
177    if ([self justFinished])
178        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentFinishedDownloading" object: self];
179   
180    //check to stop for ratio
181    if ([self isSeeding] && ((fStopRatioSetting == RATIO_CHECK && [self ratio] >= fRatioLimit)
182            || (fStopRatioSetting == RATIO_GLOBAL && [fDefaults boolForKey: @"RatioCheck"]
183            && [self ratio] >= [fDefaults floatForKey: @"RatioLimit"])))
184    {
185        [self stopTransfer];
186        [self setStopRatioSetting: RATIO_NO_CHECK];
187        fFinishedSeeding = YES;
188       
189        fStat = tr_torrentStat(fHandle);
190       
191        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStoppedForRatio" object: self];
192    }
193
194    [fProgressString setString: @""];
195    if ([self progress] < 1.0)
196        [fProgressString appendFormat: NSLocalizedString(@"%@ of %@ (%.2f%%)", "Torrent -> progress string"), [NSString stringForFileSize:
197                [self downloadedValid]], [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
198    else
199        [fProgressString appendFormat: NSLocalizedString(@"%@, uploaded %@ (Ratio: %@)", "Torrent -> progress string"), [NSString stringForFileSize:
200                [self size]], [NSString stringForFileSize: [self uploadedTotal]],
201                [NSString stringForRatioWithDownload: [self downloadedTotal] upload: [self uploadedTotal]]];
202
203    switch (fStat->status)
204    {
205        NSString * tempString;
206   
207        case TR_STATUS_PAUSE:
208            if (fFinishedSeeding)
209                tempString = NSLocalizedString(@"Seeding complete", "Torrent -> status string");
210            else if (fWaitToStart)
211                tempString = [NSLocalizedString(@"Waiting to start", "Torrent -> status string") stringByAppendingEllipsis];
212            else
213                tempString = NSLocalizedString(@"Paused", "Torrent -> status string");
214           
215            [fStatusString setString: tempString];
216            [fShortStatusString setString: tempString];
217           
218            break;
219
220        case TR_STATUS_CHECK:
221            tempString = [NSLocalizedString(@"Checking existing files", "Torrent -> status string") stringByAppendingEllipsis];
222           
223            [fStatusString setString: tempString];
224            [fShortStatusString setString: tempString];
225            [fRemainingTimeString setString: tempString];
226           
227            break;
228
229        case TR_STATUS_DOWNLOAD:
230            [fStatusString setString: @""];
231            if ([self totalPeers] > 1)
232                [fStatusString appendFormat: NSLocalizedString(@"Downloading from %d of %d peers", "Torrent -> status string"), [self peersUploading], [self totalPeers]];
233            else
234                [fStatusString appendFormat: NSLocalizedString(@"Downloading from %d of %d peer", "Torrent -> status string"), [self peersUploading], [self totalPeers]];
235           
236            [fRemainingTimeString setString: @""];
237            int eta = [self eta];
238            if (eta < 0)
239            {
240                [fRemainingTimeString setString: NSLocalizedString(@"Unknown", "Torrent -> remaining time")];
241                [fProgressString appendString: NSLocalizedString(@" - remaining time unknown", "Torrent -> progress string")];
242            }
243            else
244            {
245                if (eta < 60)
246                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d sec", "Torrent -> remaining time"), eta];
247                else if (eta < 3600) //60 * 60
248                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d min %02d sec", "Torrent -> remaining time"), eta / 60, eta % 60];
249                else if (eta < 86400) //24 * 60 * 60
250                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d hr %02d min", "Torrent -> remaining time"), eta / 3600, (eta / 60) % 60];
251                else
252                {
253                    if (eta / 86400 > 1)
254                        [fRemainingTimeString appendFormat: NSLocalizedString(@"%d days %d hr", "Torrent -> remaining time"),
255                                                eta / 86400, (eta / 3600) % 24];
256                    else
257                        [fRemainingTimeString appendFormat: NSLocalizedString(@"%d day %d hr", "Torrent -> remaining time"),
258                                                eta / 86400, (eta / 3600) % 24];
259                }
260               
261                [fProgressString appendFormat: NSLocalizedString(@" - %@ remaining", "Torrent -> progress string"), fRemainingTimeString];
262            }
263           
264            break;
265
266        case TR_STATUS_SEED:
267            [fStatusString setString: @""];
268            if ([self totalPeers] > 1)
269                [fStatusString appendFormat: NSLocalizedString(@"Seeding to %d of %d peers", "Torrent -> status string"), [self peersDownloading], [self totalPeers]];
270            else
271                [fStatusString appendFormat: NSLocalizedString(@"Seeding to %d of %d peer", "Torrent -> status string"), [self peersDownloading], [self totalPeers]];
272           
273            break;
274
275        case TR_STATUS_STOPPING:
276            tempString = [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
277       
278            [fStatusString setString: tempString];
279            [fShortStatusString setString: tempString];
280           
281            break;
282    }
283   
284    if (fStat->error & TR_ETRACKER)
285    {
286        [fStatusString setString: [NSLocalizedString(@"Error: ", "Torrent -> status string") stringByAppendingString: [NSString stringWithUTF8String: fStat->trackerError]]];
287        if (!fError && [self isActive])
288        {
289            fError = YES;
290            if (![self isSeeding])
291                [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
292        }
293    }
294    else
295    {
296        if (fError)
297            fError = NO;
298    }
299
300    if ([self isActive])
301    {
302        NSString * stringToAppend = @"";
303        if ([self progress] < 1.0)
304        {
305            stringToAppend = [NSString stringWithFormat: NSLocalizedString(@"DL: %@, ", "Torrent -> status string"), [NSString stringForSpeed: [self downloadRate]]];
306            [fShortStatusString setString: @""];
307        }
308        else
309        {
310            NSString * ratioString = [NSString stringForRatioWithDownload: [self downloadedTotal]
311                                                upload: [self uploadedTotal]];
312       
313            [fShortStatusString setString: [NSString stringWithFormat: NSLocalizedString(@"Ratio: %@, ", "Torrent -> status string"), ratioString]];
314            [fRemainingTimeString setString: [NSLocalizedString(@"Ratio: ", "Torrent -> status string") stringByAppendingString: ratioString]];
315        }
316       
317        stringToAppend = [stringToAppend stringByAppendingString: [NSLocalizedString(@"UL: ", "Torrent -> status string") stringByAppendingString:
318                                                [NSString stringForSpeed: [self uploadRate]]]];
319
320        [fStatusString appendFormat: @" - %@", stringToAppend];
321        [fShortStatusString appendString: stringToAppend];
322    }
323}
324
325- (NSDictionary *) infoForCurrentView
326{
327    NSMutableDictionary * info = [NSMutableDictionary dictionaryWithObjectsAndKeys:
328                                    [self name], @"Name",
329                                    [NSNumber numberWithBool: [self isSeeding]], @"Seeding",
330                                    [NSNumber numberWithFloat: [self progress]], @"Progress",
331                                    [NSNumber numberWithBool: [self isActive]], @"Active",
332                                    [NSNumber numberWithBool: [self isError]], @"Error", nil];
333   
334    if (![fDefaults boolForKey: @"SmallView"])
335    {
336        [info setObject: fIconFlipped forKey: @"Icon"];
337        [info setObject: [self progressString] forKey: @"ProgressString"];
338        [info setObject: [self statusString] forKey: @"StatusString"];
339    }
340    else
341    {
342        [info setObject: fIconSmall forKey: @"Icon"];
343        [info setObject: [self remainingTimeString] forKey: @"RemainingTimeString"];
344        [info setObject: [self shortStatusString] forKey: @"ShortStatusString"];
345    }
346   
347    if ([fDefaults boolForKey: @"UseAdvancedBar"])
348        [info setObject: [self advancedBar] forKey: @"AdvancedBar"];
349   
350    return info;
351}
352
353- (void) startTransfer
354{
355    fWaitToStart = NO;
356    fFinishedSeeding = NO;
357   
358    if (![self isActive] && [self remainingDiskSpaceForTorrent])
359        tr_torrentStart(fHandle);
360}
361
362- (void) stopTransfer
363{
364    fError = NO;
365   
366    if ([self isActive])
367    {
368        BOOL wasSeeding = [self isSeeding];
369   
370        tr_torrentStop(fHandle);
371
372        if (!wasSeeding)
373            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
374    }
375}
376
377- (void) stopTransferForQuit
378{
379    if ([self isActive])
380        tr_torrentStop(fHandle);
381}
382
383- (void) removeForever
384{
385    if (fPrivateTorrent)
386        tr_torrentRemoveSaved(fHandle);
387}
388
389- (void) sleep
390{
391    if ((fResumeOnWake = [self isActive]))
392        tr_torrentStop(fHandle);
393}
394
395- (void) wakeUp
396{
397    if (fResumeOnWake)
398        tr_torrentStart(fHandle);
399}
400
401- (float) ratio
402{
403    float downloaded = [self downloadedTotal];
404    return downloaded > 0 ? (float)[self uploadedTotal] / downloaded : -1;
405}
406
407- (int) stopRatioSetting
408{
409        return fStopRatioSetting;
410}
411
412- (void) setStopRatioSetting: (int) setting
413{
414    fStopRatioSetting = setting;
415}
416
417- (float) ratioLimit
418{
419    return fRatioLimit;
420}
421
422- (void) setRatioLimit: (float) limit
423{
424    if (limit >= 0)
425        fRatioLimit = limit;
426}
427
428- (void) setWaitToStart: (BOOL) wait
429{
430    fWaitToStart = wait;
431}
432
433- (BOOL) waitingToStart
434{
435    return fWaitToStart;
436}
437
438- (void) revealData
439{
440    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
441}
442
443- (void) revealPublicTorrent
444{
445    if (fPublicTorrent)
446        [[NSWorkspace sharedWorkspace] selectFile: fPublicTorrentLocation inFileViewerRootedAtPath: nil];
447}
448
449- (void) trashData
450{
451    [self trashFile: [self dataLocation]];
452}
453
454- (void) trashTorrent
455{
456    if (fPublicTorrent)
457        [self trashFile: [self publicTorrentLocation]];
458}
459
460- (BOOL) remainingDiskSpaceForTorrent
461{
462    if ([self progress] >= 1.0)
463        return YES;
464   
465    NSString * location = [self dataLocation],
466                * volume = [[[NSFileManager defaultManager] componentsToDisplayForPath: location] objectAtIndex: 0];
467    NSDictionary * fsAttributes = [[NSFileManager defaultManager] fileSystemAttributesAtPath: location];
468    uint64_t remainingSpace = [[fsAttributes objectForKey: NSFileSystemFreeSize] unsignedLongLongValue],
469            torrentRemaining = [self size] - (uint64_t)[self downloadedValid];
470   
471    NSLog(@"Volume: %@", volume);
472    NSLog(@"Remaining disk space: %qu (%@)", remainingSpace, [NSString stringForFileSize: remainingSpace]);
473    NSLog(@"Torrent remaining size: %qu (%@)", torrentRemaining, [NSString stringForFileSize: torrentRemaining]);
474   
475    if (volume && remainingSpace <= torrentRemaining)
476    {
477        NSAlert * alert = [[NSAlert alloc] init];
478        [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Not enough remaining disk space to download \"%@\" completely.", "Torrent file disk space alert -> title"),
479                                    [self name]]];
480        [alert setInformativeText: [NSString stringWithFormat:
481                        NSLocalizedString(@"The transfer will be paused. Clear up space on %@ to continue.", "Torrent file disk space alert -> message"), volume]];
482        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Torrent file disk space alert -> button")];
483        [alert addButtonWithTitle: NSLocalizedString(@"Download Anyway", "Torrent file disk space alert -> button")];
484       
485        if ([alert runModal] == NSAlertFirstButtonReturn)
486        {
487            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
488            return NO;
489        }
490        else
491            return YES;
492    }
493    return YES;
494}
495
496- (NSImage *) icon
497{
498    return fIcon;
499}
500
501- (NSImage *) iconFlipped
502{
503    return fIconFlipped;
504}
505
506- (NSImage *) iconSmall
507{
508    return fIconSmall;
509}
510
511- (NSString *) name
512{
513    return [NSString stringWithUTF8String: fInfo->name];
514}
515
516- (uint64_t) size
517{
518    return fInfo->totalSize;
519}
520
521- (NSString *) tracker
522{
523    return [NSString stringWithFormat: @"%s:%d", fInfo->trackerAddress, fInfo->trackerPort];
524}
525
526- (NSString *) announce
527{
528    return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
529}
530
531- (int) pieceSize
532{
533    return fInfo->pieceSize;
534}
535
536- (int) pieceCount
537{
538    return fInfo->pieceCount;
539}
540
541- (NSString *) hashString
542{
543    return [NSString stringWithUTF8String: fInfo->hashString];
544}
545
546- (NSString *) torrentLocation
547{
548    return [NSString stringWithUTF8String: fInfo->torrent];
549}
550
551- (NSString *) publicTorrentLocation
552{
553    return fPublicTorrentLocation;
554}
555
556- (NSString *) torrentLocationString
557{
558    return fPrivateTorrent ? NSLocalizedString(@"Transmission Support Folder", "Torrent -> location") : [fPublicTorrentLocation stringByAbbreviatingWithTildeInPath];
559}
560
561- (NSString *) dataLocation
562{
563    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
564}
565
566- (BOOL) publicTorrent
567{
568    return fPublicTorrent;
569}
570
571- (BOOL) privateTorrent
572{
573    return fPrivateTorrent;
574}
575
576- (NSString *) stateString
577{
578    switch( fStat->status )
579    {
580        case TR_STATUS_PAUSE:
581            return NSLocalizedString(@"Paused", "Torrent -> status string");
582            break;
583
584        case TR_STATUS_CHECK:
585            return [NSLocalizedString(@"Checking existing files", "Torrent -> status string") stringByAppendingEllipsis];
586            break;
587
588        case TR_STATUS_DOWNLOAD:
589            return NSLocalizedString(@"Downloading", "Torrent -> status string");
590            break;
591
592        case TR_STATUS_SEED:
593            return NSLocalizedString(@"Seeding", "Torrent -> status string");
594            break;
595
596        case TR_STATUS_STOPPING:
597            return [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
598            break;
599       
600        default:
601            return NSLocalizedString(@"N/A", "Torrent -> status string");
602    }
603}
604
605- (float) progress
606{
607    return fStat->progress;
608}
609
610- (int) eta
611{
612    return fStat->eta;
613}
614
615- (BOOL) isActive
616{
617    return fStat->status & TR_STATUS_ACTIVE;
618}
619
620- (BOOL) isSeeding
621{
622    return fStat->status == TR_STATUS_SEED;
623}
624
625- (BOOL) isPaused
626{
627    return fStat->status == TR_STATUS_PAUSE;
628}
629
630- (BOOL) isError
631{
632    return fStat->error & TR_ETRACKER;
633}
634
635- (BOOL) justFinished
636{
637    return tr_getFinished(fHandle);
638}
639
640- (NSArray *) peers
641{
642    int totalPeers, i;
643    tr_peer_stat_t * peers = tr_torrentPeers(fHandle, & totalPeers);
644   
645    NSMutableArray * peerDics = [NSMutableArray arrayWithCapacity: totalPeers];
646    tr_peer_stat_t peer;
647    NSString * client;
648    for (i = 0; i < totalPeers; i++)
649    {
650        peer = peers[i];
651        [peerDics addObject: [NSDictionary dictionaryWithObjectsAndKeys:
652            [NSNumber numberWithBool: peer.isConnected], @"Connected",
653            [NSNumber numberWithBool: peer.isIncoming], @"Incoming",
654            [NSString stringWithCString: (char *) peer.addr encoding: NSUTF8StringEncoding], @"IP",
655            [NSString stringWithCString: (char *) peer.client encoding: NSUTF8StringEncoding], @"Client",
656            [NSNumber numberWithBool: peer.isDownloading], @"UL To",
657            [NSNumber numberWithBool: peer.isUploading], @"DL From", nil]];
658    }
659   
660    tr_torrentPeersFree(peers, totalPeers);
661   
662    return peerDics;
663}
664
665- (NSString *) progressString
666{
667    return fProgressString;
668}
669
670- (NSString *) statusString
671{
672    return fStatusString;
673}
674
675- (NSString *) shortStatusString
676{
677    return fShortStatusString;
678}
679
680- (NSString *) remainingTimeString
681{
682    return fRemainingTimeString;
683}
684
685- (int) seeders
686{
687    return fStat->seeders;
688}
689
690- (int) leechers
691{
692    return fStat->leechers;
693}
694
695- (int) totalPeers
696{
697    return fStat->peersTotal;
698}
699
700- (int) totalPeersIncoming
701{
702    return fStat->peersIncoming;
703}
704
705- (int) totalPeersOutgoing
706{
707    return [self totalPeers] - [self totalPeersIncoming];
708}
709
710//peers uploading to you
711- (int) peersUploading
712{
713    return fStat->peersUploading;
714}
715
716//peers downloading from you
717- (int) peersDownloading
718{
719    return fStat->peersDownloading;
720}
721
722- (float) downloadRate
723{
724    return fStat->rateDownload;
725}
726
727- (float) uploadRate
728{
729    return fStat->rateUpload;
730}
731
732- (float) downloadedValid
733{
734    return [self progress] * [self size];
735}
736
737- (uint64_t) downloadedTotal
738{
739    return fStat->downloaded;
740}
741
742- (uint64_t) uploadedTotal
743{
744    return fStat->uploaded;
745}
746
747- (float) swarmSpeed
748{
749    return fStat->swarmspeed;
750}
751
752- (NSNumber *) orderValue
753{
754    return [NSNumber numberWithInt: fOrderValue];
755}
756
757- (void) setOrderValue: (int) orderValue
758{
759    fOrderValue = orderValue;
760}
761
762- (NSArray *) fileList
763{
764    int count = fInfo->fileCount, i;
765    tr_file_t file;
766    NSMutableArray * files = [NSMutableArray arrayWithCapacity: count];
767   
768    for (i = 0; i < count; i++)
769    {
770        file = fInfo->files[i];
771        [files addObject: [NSDictionary dictionaryWithObjectsAndKeys:
772            [[self downloadFolder] stringByAppendingPathComponent: [NSString stringWithUTF8String: file.name]], @"Name",
773            [NSNumber numberWithUnsignedLongLong: file.length], @"Size", nil]];
774    }
775   
776    return files;
777}
778
779- (NSDate *) date
780{
781    return fDate;
782}
783
784- (NSNumber *) stateSortKey
785{
786    if (![self isActive])
787        return [NSNumber numberWithInt: 0];
788    else if ([self isSeeding])
789        return [NSNumber numberWithInt: 1];
790    else
791        return [NSNumber numberWithInt: 2];
792}
793
794- (NSNumber *) progressSortKey
795{
796    //if finished downloading sort by ratio instead of progress
797    float progress = [self progress];
798    return [NSNumber numberWithFloat: progress < 1.0 ? progress : 2.0 + [self ratio]];
799}
800
801@end
802
803
804@implementation Torrent (Private)
805
806//if a hash is given, attempt to load that; otherwise, attempt to open file at path
807- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
808        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
809        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
810        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
811        orderValue: (NSNumber *) orderValue
812{
813    if (!(self = [super init]))
814        return nil;
815
816    fLib = lib;
817    fDefaults = [NSUserDefaults standardUserDefaults];
818
819    fPrivateTorrent = privateTorrent ? [privateTorrent boolValue] : [fDefaults boolForKey: @"SavePrivateTorrent"];
820    fPublicTorrent = !fPrivateTorrent || (publicTorrent ? [publicTorrent boolValue]
821                                            : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
822
823    int error;
824    if (fPrivateTorrent && hashString)
825        fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], TR_FSAVEPRIVATE, & error);
826   
827    if (!fHandle && path)
828        fHandle = tr_torrentInit(fLib, [path UTF8String], fPrivateTorrent ? TR_FSAVEPRIVATE : 0, & error);
829
830    if (!fHandle)
831    {
832        [self release];
833        return nil;
834    }
835   
836    fInfo = tr_torrentInfo( fHandle );
837
838    if (fPublicTorrent)
839        fPublicTorrentLocation = [path retain];
840
841    fDate = date ? [date retain] : [[NSDate alloc] init];
842   
843    fStopRatioSetting = stopRatioSetting ? [stopRatioSetting intValue] : -1;
844    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
845    fFinishedSeeding = NO;
846   
847    fWaitToStart = waitToStart ? [waitToStart boolValue] : [fDefaults boolForKey: @"AutoStartDownload"];
848    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
849    fError = NO;
850   
851    NSString * fileType = fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
852    fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
853    [fIcon retain];
854   
855    fIconFlipped = [fIcon copy];
856    [fIconFlipped setFlipped: YES];
857   
858    fIconSmall = [fIconFlipped copy];
859    [fIconSmall setScalesWhenResized: YES];
860    [fIconSmall setSize: NSMakeSize(16.0, 16.0)];
861
862    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
863    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
864    fShortStatusString = [[NSMutableString alloc] initWithCapacity: 30];
865    fRemainingTimeString = [[NSMutableString alloc] initWithCapacity: 30];
866   
867    //set up advanced bar
868    fBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
869        pixelsWide: MAX_PIECES pixelsHigh: BAR_HEIGHT bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
870        isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
871   
872    fPieces = malloc(MAX_PIECES);
873    int i;
874    for (i = 0; i < MAX_PIECES; i++)
875        fPieces[i] = BLANK_PIECE;
876
877    [self update];
878    return self;
879}
880
881- (NSImage *) advancedBar
882{
883    int h, w;
884    uint32_t * p;
885    uint8_t * bitmapData = [fBitmap bitmapData];
886    int bytesPerRow = [fBitmap bytesPerRow];
887
888    int8_t * pieces = malloc(MAX_PIECES);
889    [self getAvailability: pieces size: MAX_PIECES];
890   
891    //lines 2 to 14: blue, green, or gray depending on whether we have the piece or not
892    int have = 0, avail = 0;
893    uint32_t color;
894    BOOL change;
895    for (w = 0; w < MAX_PIECES; w++)
896    {
897        change = NO;
898        if (pieces[w] < 0)
899        {
900            if (fPieces[w] != -1)
901            {
902                color = kBlue;
903                fPieces[w] = -1;
904                change = YES;
905            }
906            have++;
907        }
908        else if (pieces[w] == 0)
909        {
910            if (fPieces[w] != 0)
911            {
912                color = kGray;
913                fPieces[w] = 0;
914                change = YES;
915            }
916        }
917        else
918        {
919            if (pieces[w] == 1)
920            {
921                if (fPieces[w] != 1)
922                {
923                    color = kGreen1;
924                    fPieces[w] = 1;
925                    change = YES;
926                }
927            }
928            else if (pieces[w] == 2)
929            {
930                if (fPieces[w] != 2)
931                {
932                    color = kGreen2;
933                    fPieces[w] = 2;
934                    change = YES;
935                }
936            }
937            else
938            {
939                if (fPieces[w] != 3)
940                {
941                    color = kGreen3;
942                    fPieces[w] = 3;
943                    change = YES;
944                }
945            }
946            avail++;
947        }
948       
949        if (change)
950        {
951            //point to pixel (w, 2) and draw "vertically"
952            p = (uint32_t *)(bitmapData + 2 * bytesPerRow) + w;
953            for (h = 2; h < BAR_HEIGHT; h++)
954            {
955                p[0] = color;
956                p = (uint32_t *)((uint8_t *)p + bytesPerRow);
957            }
958        }
959    }
960   
961    //first two lines: dark blue to show progression, green to show available
962    p = (uint32_t *) bitmapData;
963    for (w = 0; w < have; w++)
964    {
965        p[w] = kBlue2;
966        p[w + bytesPerRow / 4] = kBlue2;
967    }
968    for (; w < avail + have; w++)
969    {
970        p[w] = kGreen3;
971        p[w + bytesPerRow / 4] = kGreen3;
972    }
973    for (; w < MAX_PIECES; w++)
974    {
975        p[w] = kWhite;
976        p[w + bytesPerRow / 4] = kWhite;
977    }
978   
979    free(pieces);
980   
981    //actually draw image
982    NSImage * bar = [[NSImage alloc] initWithSize: [fBitmap size]];
983    [bar addRepresentation: fBitmap];
984    [bar setScalesWhenResized: YES];
985   
986    return [bar autorelease];
987}
988
989- (void) trashFile: (NSString *) path
990{
991    //attempt to move to trash
992    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
993            source: [path stringByDeletingLastPathComponent] destination: @""
994            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
995    {
996        //if cannot trash, just delete it (will work if it is on a remote volume)
997        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
998            NSLog([@"Could not trash " stringByAppendingString: path]);
999    }
1000}
1001
1002@end
Note: See TracBrowser for help on using the repository browser.