source: trunk/macosx/Torrent.m @ 1010

Last change on this file since 1010 was 1010, checked in by livings124, 16 years ago

buttons in inspector to reveal data file and (public) torrent file

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