source: trunk/macosx/Torrent.m @ 1099

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

update info when changing directory

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