source: trunk/macosx/Torrent.m @ 1561

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

tiny consistency changes

  • Property svn:keywords set to Date Rev Author Id
File size: 47.3 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 1561 2007-03-11 22:12:01Z 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#define INVALID -99
34
35@interface Torrent (Private)
36
37- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
38        publicTorrent: (NSNumber *) publicTorrent
39        date: (NSDate *) date
40        ratioSetting: (NSNumber *) ratioSetting ratioLimit: (NSNumber *) ratioLimit
41        limitSpeedCustom: (NSNumber *) limitCustom
42        checkUpload: (NSNumber *) checkUpload uploadLimit: (NSNumber *) uploadLimit
43        checkDownload: (NSNumber *) checkDownload downloadLimit: (NSNumber *) downloadLimit
44        waitToStart: (NSNumber *) waitToStart orderValue: (NSNumber *) orderValue;
45
46- (void) createFileList;
47- (void) insertPath: (NSMutableArray *) components forSiblings: (NSMutableArray *) siblings
48        withParent: (NSMutableDictionary *) parent previousPath: (NSString *) previousPath
49        fileSize: (uint64_t) size state: (int) state flatList: (NSMutableArray *) flatList;
50- (NSImage *) advancedBar;
51- (void) trashFile: (NSString *) path;
52
53@end
54
55@implementation Torrent
56
57// Used to optimize drawing. They contain packed RGBA pixels for every color needed.
58#define BE OSSwapBigToHostConstInt32
59
60static uint32_t kRed   = BE(0xFF6450FF), //255, 100, 80
61                kBlue = BE(0x50A0FFFF), //80, 160, 255
62                kBlue2 = BE(0x1E46B4FF), //30, 70, 180
63                kGray  = BE(0x969696FF), //150, 150, 150
64                kGreen1 = BE(0x99FFCCFF), //153, 255, 204
65                kGreen2 = BE(0x66FF99FF), //102, 255, 153
66                kGreen3 = BE(0x00FF66FF), //0, 255, 102
67                kWhite = BE(0xFFFFFFFF); //255, 255, 255
68
69- (id) initWithPath: (NSString *) path forceDeleteTorrent: (BOOL) delete lib: (tr_handle_t *) lib
70{
71    self = [self initWithHash: nil path: path lib: lib
72            publicTorrent: delete ? [NSNumber numberWithBool: NO] : nil
73            date: nil
74            ratioSetting: nil ratioLimit: nil
75            limitSpeedCustom: nil
76            checkUpload: nil uploadLimit: nil
77            checkDownload: nil downloadLimit: nil
78            waitToStart: nil orderValue: nil];
79   
80    if (self)
81    {
82        fUseIncompleteFolder = [fDefaults boolForKey: @"UseIncompleteDownloadFolder"];
83        fIncompleteFolder = [[fDefaults stringForKey: @"IncompleteDownloadFolder"] copy];
84       
85        if (!fPublicTorrent)
86            [self trashFile: path];
87    }
88    return self;
89}
90
91- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle_t *) lib
92{
93    self = [self initWithHash: [history objectForKey: @"TorrentHash"]
94                path: [history objectForKey: @"TorrentPath"] lib: lib
95                publicTorrent: [history objectForKey: @"PublicCopy"]
96                date: [history objectForKey: @"Date"]
97                ratioSetting: [history objectForKey: @"RatioSetting"]
98                ratioLimit: [history objectForKey: @"RatioLimit"]
99                limitSpeedCustom: [history objectForKey: @"LimitSpeedCustom"]
100                checkUpload: [history objectForKey: @"CheckUpload"]
101                uploadLimit: [history objectForKey: @"UploadLimit"]
102                checkDownload: [history objectForKey: @"CheckDownload"]
103                downloadLimit: [history objectForKey: @"DownloadLimit"]
104                waitToStart: [history objectForKey: @"WaitToStart"]
105                orderValue: [history objectForKey: @"OrderValue"]];
106   
107    if (self)
108    {
109        //download folders
110        NSString * downloadFolder;
111        if (!(downloadFolder = [history objectForKey: @"DownloadFolder"]))
112            downloadFolder = [[fDefaults stringForKey: @"DownloadFolder"] stringByExpandingTildeInPath];
113       
114        NSNumber * useIncompleteFolder;
115        if ((useIncompleteFolder = [history objectForKey: @"UseIncompleteFolder"]))
116        {
117            if ((fUseIncompleteFolder = [useIncompleteFolder boolValue]))
118            {
119                NSString * incompleteFolder;
120                if (incompleteFolder = [history objectForKey: @"IncompleteFolder"])
121                    fIncompleteFolder = [incompleteFolder retain];
122                else
123                    fIncompleteFolder = [[[fDefaults stringForKey: @"IncompleteDownloadFolder"]
124                                            stringByExpandingTildeInPath] retain];
125            }
126        }
127        else
128            fUseIncompleteFolder = NO;
129       
130        [self setDownloadFolder: downloadFolder];
131
132        NSString * paused;
133        if (!(paused = [history objectForKey: @"Paused"]) || [paused isEqualToString: @"NO"])
134        {
135            fStat = tr_torrentStat(fHandle);
136            [self startTransfer];
137        }
138    }
139    return self;
140}
141
142- (NSDictionary *) history
143{
144    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
145                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
146                    [self hashString], @"TorrentHash",
147                    fDownloadFolder, @"DownloadFolder",
148                    [NSNumber numberWithBool: fUseIncompleteFolder], @"UseIncompleteFolder",
149                    [self isActive] ? @"NO" : @"YES", @"Paused",
150                    [self date], @"Date",
151                    [NSNumber numberWithInt: fRatioSetting], @"RatioSetting",
152                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit",
153                    [NSNumber numberWithInt: fCheckUpload], @"CheckUpload",
154                    [NSNumber numberWithInt: fUploadLimit], @"UploadLimit",
155                    [NSNumber numberWithInt: fCheckDownload], @"CheckDownload",
156                    [NSNumber numberWithInt: fDownloadLimit], @"DownloadLimit",
157                    [NSNumber numberWithBool: fWaitToStart], @"WaitToStart",
158                    [self orderValue], @"OrderValue", nil];
159   
160    if (fUseIncompleteFolder)
161        [history setObject: fIncompleteFolder forKey: @"IncompleteFolder"];
162
163    if (fPublicTorrent)
164        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
165   
166    return history;
167}
168
169- (void) dealloc
170{
171    if (fHandle)
172    {
173        tr_torrentClose(fLib, fHandle);
174       
175        if (fDownloadFolder)
176            [fDownloadFolder release];
177        if (fIncompleteFolder)
178            [fIncompleteFolder release];
179       
180        if (fPublicTorrentLocation)
181            [fPublicTorrentLocation release];
182       
183        tr_torrentRemoveSaved(fHandle);
184       
185        [fDate release];
186       
187        if (fAnnounceDate)
188            [fAnnounceDate release];
189       
190        [fIcon release];
191        [fIconFlipped release];
192        [fIconSmall release];
193       
194        [fProgressString release];
195        [fStatusString release];
196        [fShortStatusString release];
197        [fRemainingTimeString release];
198       
199        [fFileList release];
200        [fFileFlatList release];
201       
202        [fBitmap release];
203        free(fPieces);
204    }
205    [super dealloc];
206}
207
208- (void) setDownloadFolder: (NSString *) path
209{
210    if (path)
211        fDownloadFolder = [path copy];
212   
213    if (!fUseIncompleteFolder || [[NSFileManager defaultManager] fileExistsAtPath:
214                                    [path stringByAppendingPathComponent: [self name]]])
215        tr_torrentSetFolder(fHandle, [path UTF8String]);
216    else
217        tr_torrentSetFolder(fHandle, [fIncompleteFolder UTF8String]);
218}
219
220- (NSString *) downloadFolder
221{
222    return [NSString stringWithUTF8String: tr_torrentGetFolder(fHandle)];
223}
224
225- (void) getAvailability: (int8_t *) tab size: (int) size
226{
227    tr_torrentAvailability(fHandle, tab, size);
228}
229
230- (void) getAmountFinished: (float *) tab size: (int) size
231{
232    tr_torrentAmountFinished(fHandle, tab, size);
233}
234
235- (void) update
236{
237    fStat = tr_torrentStat(fHandle);
238   
239    //notification when downloading finished
240    if ([self justFinished])
241    {
242        BOOL canMove = YES;
243       
244        //move file from incomplete folder to download folder
245        if (fUseIncompleteFolder && ![[self downloadFolder] isEqualToString: fDownloadFolder]
246            && (canMove = [self alertForMoveFolderAvailable]))
247        {
248            tr_torrentStop(fHandle);
249            if ([[NSFileManager defaultManager] movePath: [[self downloadFolder] stringByAppendingPathComponent: [self name]]
250                                    toPath: [fDownloadFolder stringByAppendingPathComponent: [self name]] handler: nil])
251                tr_torrentSetFolder(fHandle, [fDownloadFolder UTF8String]);
252            tr_torrentStart(fHandle);
253        }
254       
255        if (!canMove)
256        {
257            fUseIncompleteFolder = NO;
258           
259            [fDownloadFolder release];
260            fDownloadFolder = fIncompleteFolder;
261            fIncompleteFolder = nil;
262        }
263       
264        fStat = tr_torrentStat(fHandle);
265        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentFinishedDownloading" object: self];
266    }
267   
268    //check to stop for ratio
269    if ([self isSeeding] && ((fRatioSetting == NSOnState && [self ratio] >= fRatioLimit)
270            || (fRatioSetting == NSMixedState && [fDefaults boolForKey: @"RatioCheck"]
271                && [self ratio] >= [fDefaults floatForKey: @"RatioLimit"])))
272    {
273        [self stopTransfer];
274        fStat = tr_torrentStat(fHandle);
275       
276        fFinishedSeeding = YES;
277       
278        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStoppedForRatio" object: self];
279    }
280
281    [fProgressString setString: @""];
282    if (![self allDownloaded])
283        [fProgressString appendFormat: NSLocalizedString(@"%@ of %@ (%.2f%%)", "Torrent -> progress string"),
284                            [NSString stringForFileSize: [self downloadedValid]],
285                            [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
286    else
287        [fProgressString appendFormat: NSLocalizedString(@"%@, uploaded %@ (Ratio: %@)", "Torrent -> progress string"),
288                [NSString stringForFileSize: [self size]], [NSString stringForFileSize: [self uploadedTotal]],
289                [NSString stringForRatio: [self ratio]]];
290
291    BOOL wasChecking = fChecking;
292    fChecking = NO;
293    switch (fStat->status)
294    {
295        NSString * tempString;
296       
297        case TR_STATUS_PAUSE:
298            if (fWaitToStart)
299            {
300                tempString = ![self allDownloaded]
301                        ? [NSLocalizedString(@"Waiting to download", "Torrent -> status string") stringByAppendingEllipsis]
302                        : [NSLocalizedString(@"Waiting to seed", "Torrent -> status string") stringByAppendingEllipsis];
303            }
304            else if (fFinishedSeeding)
305                tempString = NSLocalizedString(@"Seeding complete", "Torrent -> status string");
306            else
307                tempString = NSLocalizedString(@"Paused", "Torrent -> status string");
308           
309            [fStatusString setString: tempString];
310            [fShortStatusString setString: tempString];
311           
312            break;
313
314        case TR_STATUS_CHECK:
315            tempString = [NSLocalizedString(@"Checking existing files", "Torrent -> status string") stringByAppendingEllipsis];
316           
317            [fStatusString setString: tempString];
318            [fShortStatusString setString: tempString];
319            [fRemainingTimeString setString: tempString];
320           
321            fChecking = YES;
322           
323            break;
324
325        case TR_STATUS_DOWNLOAD:
326            [fStatusString setString: @""];
327            if ([self totalPeers] != 1)
328                [fStatusString appendFormat: NSLocalizedString(@"Downloading from %d of %d peers",
329                                                "Torrent -> status string"), [self peersUploading], [self totalPeers]];
330            else
331                [fStatusString appendFormat: NSLocalizedString(@"Downloading from %d of 1 peer",
332                                                "Torrent -> status string"), [self peersUploading]];
333           
334            [fRemainingTimeString setString: @""];
335            int eta = [self eta];
336            if (eta < 0)
337            {
338                [fRemainingTimeString setString: NSLocalizedString(@"Unknown", "Torrent -> remaining time")];
339                [fProgressString appendString: NSLocalizedString(@" - remaining time unknown", "Torrent -> progress string")];
340            }
341            else
342            {
343                if (eta < 60)
344                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d sec", "Torrent -> remaining time"), eta];
345                else if (eta < 3600) //60 * 60
346                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d min %02d sec", "Torrent -> remaining time"),
347                                                            eta / 60, eta % 60];
348                else if (eta < 86400) //24 * 60 * 60
349                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d hr %02d min", "Torrent -> remaining time"),
350                                                            eta / 3600, (eta / 60) % 60];
351                else
352                {
353                    if (eta / 86400 > 1)
354                        [fRemainingTimeString appendFormat: NSLocalizedString(@"%d days %d hr", "Torrent -> remaining time"),
355                                                                eta / 86400, (eta / 3600) % 24];
356                    else
357                        [fRemainingTimeString appendFormat: NSLocalizedString(@"%d day %d hr", "Torrent -> remaining time"),
358                                                                eta / 86400, (eta / 3600) % 24];
359                }
360               
361                [fProgressString appendFormat: NSLocalizedString(@" - %@ remaining", "Torrent -> progress string"),
362                                                                    fRemainingTimeString];
363            }
364           
365            break;
366
367        case TR_STATUS_SEED:
368            [fStatusString setString: @""];
369            if ([self totalPeers] != 1)
370                [fStatusString appendFormat: NSLocalizedString(@"Seeding to %d of %d peers", "Torrent -> status string"),
371                                                [self peersDownloading], [self totalPeers]];
372            else
373                [fStatusString appendFormat: NSLocalizedString(@"Seeding to %d of 1 peer", "Torrent -> status string"),
374                                                [self peersDownloading]];
375           
376            break;
377
378        case TR_STATUS_STOPPING:
379            tempString = [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
380       
381            [fStatusString setString: tempString];
382            [fShortStatusString setString: tempString];
383           
384            break;
385    }
386   
387    if (wasChecking && !fChecking)
388        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
389   
390    if (fStat->error)
391    {
392        [fStatusString setString: [NSLocalizedString(@"Error: ", "Torrent -> status string") stringByAppendingString:
393                                    [self errorMessage]]];
394    }
395   
396    BOOL wasError = fError;
397    if ((fError = fStat->cannotConnect))
398    {
399        if (!wasError && [self isActive])
400            [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
401    }
402
403    if ([self isActive] && fStat->status != TR_STATUS_CHECK )
404    {
405        NSString * stringToAppend = @"";
406        if (![self allDownloaded])
407        {
408            stringToAppend = [NSString stringWithFormat: NSLocalizedString(@"DL: %@, ", "Torrent -> status string"),
409                                [NSString stringForSpeed: [self downloadRate]]];
410            [fShortStatusString setString: @""];
411        }
412        else
413        {
414            NSString * ratioString = [NSString stringForRatio: [self ratio]];
415       
416            [fShortStatusString setString: [NSString stringWithFormat: NSLocalizedString(@"Ratio: %@, ",
417                                            "Torrent -> status string"), ratioString]];
418            [fRemainingTimeString setString: [NSLocalizedString(@"Ratio: ", "Torrent -> status string")
419                                                stringByAppendingString: ratioString]];
420        }
421       
422        stringToAppend = [stringToAppend stringByAppendingString: [NSLocalizedString(@"UL: ", "Torrent -> status string")
423                                            stringByAppendingString: [NSString stringForSpeed: [self uploadRate]]]];
424
425        [fStatusString appendFormat: @" - %@", stringToAppend];
426        [fShortStatusString appendString: stringToAppend];
427    }
428}
429
430- (NSDictionary *) infoForCurrentView
431{
432    NSMutableDictionary * info = [NSMutableDictionary dictionaryWithObjectsAndKeys:
433                                    [self name], @"Name",
434                                    [NSNumber numberWithBool: [self isSeeding]], @"Seeding",
435                                    [NSNumber numberWithFloat: [self progress]], @"Progress",
436                                    [NSNumber numberWithBool: [self isActive]], @"Active",
437                                    [NSNumber numberWithBool: [self isError]], @"Error", nil];
438   
439    if (![fDefaults boolForKey: @"SmallView"])
440    {
441        [info setObject: fIconFlipped forKey: @"Icon"];
442        [info setObject: [self progressString] forKey: @"ProgressString"];
443        [info setObject: [self statusString] forKey: @"StatusString"];
444    }
445    else
446    {
447        [info setObject: fIconSmall forKey: @"Icon"];
448        [info setObject: [self remainingTimeString] forKey: @"RemainingTimeString"];
449        [info setObject: [self shortStatusString] forKey: @"ShortStatusString"];
450    }
451   
452    if ([fDefaults boolForKey: @"UseAdvancedBar"])
453        [info setObject: [self advancedBar] forKey: @"AdvancedBar"];
454   
455    return info;
456}
457
458- (void) startTransfer
459{
460    fWaitToStart = NO;
461    fFinishedSeeding = NO;
462   
463    if (![self isActive] && [self alertForFolderAvailable] && [self alertForRemainingDiskSpace])
464    {
465        tr_torrentStart(fHandle);
466        [self update];
467    }
468}
469
470- (void) stopTransfer
471{
472    fError = NO;
473    fWaitToStart = NO;
474   
475    if ([self isActive])
476    {
477        tr_torrentStop(fHandle);
478        [self update];
479
480        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
481    }
482}
483
484- (void) stopTransferForQuit
485{
486    if ([self isActive])
487        tr_torrentStop(fHandle);
488}
489
490- (void) sleep
491{
492    if ((fResumeOnWake = [self isActive]))
493        tr_torrentStop(fHandle);
494}
495
496- (void) wakeUp
497{
498    if (fResumeOnWake)
499        tr_torrentStart(fHandle);
500}
501
502- (void) announce
503{
504    if (![self isActive])
505        return;
506   
507    tr_manualUpdate(fHandle);
508   
509    if (fAnnounceDate)
510        [fAnnounceDate release];
511    fAnnounceDate = [[NSDate date] retain];
512}
513
514- (NSDate *) announceDate
515{
516    return fAnnounceDate;
517}
518
519- (BOOL) allDownloaded
520{
521    return [self progress] >= 1.0;
522}
523
524- (float) ratio
525{
526    return fStat->ratio;
527}
528
529- (int) ratioSetting
530{
531        return fRatioSetting;
532}
533
534- (void) setRatioSetting: (int) setting
535{
536    fRatioSetting = setting;
537}
538
539- (float) ratioLimit
540{
541    return fRatioLimit;
542}
543
544- (void) setRatioLimit: (float) limit
545{
546    if (limit >= 0)
547        fRatioLimit = limit;
548}
549
550- (int) checkUpload
551{
552    return fCheckUpload;
553}
554
555- (void) setCheckUpload: (int) setting
556{
557    fCheckUpload = setting;
558    [self updateSpeedSetting];
559}
560
561- (int) uploadLimit
562{
563    return fUploadLimit;
564}
565
566- (void) setUploadLimit: (int) limit
567{
568    fUploadLimit = limit;
569    [self updateSpeedSetting];
570}
571
572- (int) checkDownload
573{
574    return fCheckDownload;
575}
576
577- (void) setCheckDownload: (int) setting
578{
579    fCheckDownload = setting;
580    [self updateSpeedSetting];
581}
582
583- (int) downloadLimit
584{
585    return fDownloadLimit;
586}
587
588- (void) setDownloadLimit: (int) limit
589{
590    fDownloadLimit = limit;
591    [self updateSpeedSetting];
592}
593
594- (void) updateSpeedSetting
595{
596    tr_setUseCustomUpload(fHandle, fCheckUpload != NSMixedState);
597    tr_setUploadLimit(fHandle, fCheckUpload == NSOnState ? fUploadLimit : -1);
598   
599    tr_setUseCustomDownload(fHandle, fCheckDownload != NSMixedState);
600    tr_setDownloadLimit(fHandle, fCheckDownload == NSOnState ? fDownloadLimit : -1);
601}
602
603- (void) setWaitToStart: (BOOL) wait
604{
605    fWaitToStart = wait;
606}
607
608- (BOOL) waitingToStart
609{
610    return fWaitToStart;
611}
612
613- (void) revealData
614{
615    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
616}
617
618- (void) revealPublicTorrent
619{
620    if (fPublicTorrent)
621        [[NSWorkspace sharedWorkspace] selectFile: fPublicTorrentLocation inFileViewerRootedAtPath: nil];
622}
623
624- (void) trashData
625{
626    [self trashFile: [self dataLocation]];
627}
628
629- (void) trashTorrent
630{
631    if (fPublicTorrent)
632        [self trashFile: [self publicTorrentLocation]];
633}
634
635- (BOOL) alertForRemainingDiskSpace
636{
637    if ([self allDownloaded])
638        return YES;
639   
640    NSString * volumeName = [[[NSFileManager defaultManager] componentsToDisplayForPath: [self downloadFolder]]
641                                                                                                objectAtIndex: 0];
642    NSDictionary * fsAttributes = [[NSFileManager defaultManager] fileSystemAttributesAtPath: [self downloadFolder]];
643    uint64_t remainingSpace = [[fsAttributes objectForKey: NSFileSystemFreeSize] unsignedLongLongValue],
644            torrentRemaining = [self size] - (uint64_t)[self downloadedValid];
645   
646    /*NSLog(@"Volume: %@", volumeName);
647    NSLog(@"Remaining disk space: %qu (%@)", remainingSpace, [NSString stringForFileSize: remainingSpace]);
648    NSLog(@"Torrent remaining size: %qu (%@)", torrentRemaining, [NSString stringForFileSize: torrentRemaining]);*/
649   
650    if (volumeName && remainingSpace <= torrentRemaining)
651    {
652        NSAlert * alert = [[NSAlert alloc] init];
653        [alert setMessageText: [NSString stringWithFormat:
654                                NSLocalizedString(@"Not enough remaining disk space to download \"%@\" completely.",
655                                    "Torrent file disk space alert -> title"), [self name]]];
656        [alert setInformativeText: [NSString stringWithFormat:
657                        NSLocalizedString(@"The transfer will be paused. Clear up space on \"%@\" to continue.",
658                                            "Torrent file disk space alert -> message"), volumeName]];
659        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Torrent file disk space alert -> button")];
660        [alert addButtonWithTitle: NSLocalizedString(@"Download Anyway", "Torrent file disk space alert -> button")];
661       
662        BOOL ret = [alert runModal] != NSAlertFirstButtonReturn;
663       
664        [alert release];
665       
666        return ret;
667    }
668    return YES;
669}
670
671- (BOOL) alertForFolderAvailable
672{
673    if (access(tr_torrentGetFolder(fHandle), 0))
674    {
675        NSAlert * alert = [[NSAlert alloc] init];
676        [alert setMessageText: [NSString stringWithFormat:
677                                NSLocalizedString(@"The folder for downloading \"%@\" cannot be found.",
678                                    "Folder cannot be found alert -> title"), [self name]]];
679        [alert setInformativeText: [NSString stringWithFormat:
680                        NSLocalizedString(@"\"%@\" cannot be found. The transfer will be paused.",
681                                            "Folder cannot be found alert -> message"), [self downloadFolder]]];
682        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Folder cannot be found alert -> button")];
683        [alert addButtonWithTitle: [NSLocalizedString(@"Choose New Location",
684                                    "Folder cannot be found alert -> location button") stringByAppendingEllipsis]];
685       
686        if ([alert runModal] != NSAlertFirstButtonReturn)
687        {
688            NSOpenPanel * panel = [NSOpenPanel openPanel];
689           
690            [panel setPrompt: NSLocalizedString(@"Select", "Folder cannot be found alert -> prompt")];
691            [panel setAllowsMultipleSelection: NO];
692            [panel setCanChooseFiles: NO];
693            [panel setCanChooseDirectories: YES];
694            [panel setCanCreateDirectories: YES];
695
696            [panel setMessage: [NSString stringWithFormat: NSLocalizedString(@"Select the download folder for \"%@\"",
697                                "Folder cannot be found alert -> select destination folder"), [self name]]];
698           
699            [[NSNotificationCenter defaultCenter] postNotificationName: @"MakeWindowKey" object: nil];
700            [panel beginSheetForDirectory: nil file: nil types: nil modalForWindow: [NSApp keyWindow] modalDelegate: self
701                    didEndSelector: @selector(destinationChoiceClosed:returnCode:contextInfo:) contextInfo: nil];
702        }
703       
704        [alert release];
705       
706        return NO;
707    }
708   
709    return YES;
710}
711
712- (void) destinationChoiceClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) context
713{
714    if (code != NSOKButton)
715        return;
716   
717    NSString * folder = [[openPanel filenames] objectAtIndex: 0];
718    if (fUseIncompleteFolder)
719    {
720        [fIncompleteFolder release];
721        fIncompleteFolder = [folder retain];
722        [self setDownloadFolder: nil];
723    }
724    else
725    {
726        [fDownloadFolder release];
727        fDownloadFolder = folder;
728        [self setDownloadFolder: fDownloadFolder];
729    }
730   
731    [self startTransfer];
732    [self update];
733   
734    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateInfoSettings" object: nil];
735}
736
737- (BOOL) alertForMoveFolderAvailable
738{
739    if (access([fDownloadFolder UTF8String], 0))
740    {
741        NSAlert * alert = [[NSAlert alloc] init];
742        [alert setMessageText: [NSString stringWithFormat:
743                                NSLocalizedString(@"The folder for moving the completed \"%@\" cannot be found.",
744                                    "Move folder cannot be found alert -> title"), [self name]]];
745        [alert setInformativeText: [NSString stringWithFormat:
746                                NSLocalizedString(@"\"%@\" cannot be found. The file will remain in its current location.",
747                                    "Move folder cannot be found alert -> message"), fDownloadFolder]];
748        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Move folder cannot be found alert -> button")];
749       
750        [alert runModal];
751        [alert release];
752       
753        return NO;
754    }
755   
756    return YES;
757}
758
759- (NSImage *) icon
760{
761    return fIcon;
762}
763
764- (NSImage *) iconFlipped
765{
766    return fIconFlipped;
767}
768
769- (NSImage *) iconSmall
770{
771    return fIconSmall;
772}
773
774- (NSString *) name
775{
776    return [NSString stringWithUTF8String: fInfo->name];
777}
778
779- (uint64_t) size
780{
781    return fInfo->totalSize;
782}
783
784- (NSString *) trackerAddress
785{
786    return [NSString stringWithFormat: @"http://%s:%d", fStat->tracker->address, fStat->tracker->port];
787}
788
789- (NSString *) trackerAddressAnnounce
790{
791    return [NSString stringWithUTF8String: fStat->tracker->announce];
792}
793
794- (NSString *) comment
795{
796    return [NSString stringWithUTF8String: fInfo->comment];
797}
798
799- (NSString *) creator
800{
801    return [NSString stringWithUTF8String: fInfo->creator];
802}
803
804- (NSDate *) dateCreated
805{
806    int date = fInfo->dateCreated;
807    return date > 0 ? [NSDate dateWithTimeIntervalSince1970: date] : nil;
808}
809
810- (int) pieceSize
811{
812    return fInfo->pieceSize;
813}
814
815- (int) pieceCount
816{
817    return fInfo->pieceCount;
818}
819
820- (NSString *) hashString
821{
822    return [NSString stringWithUTF8String: fInfo->hashString];
823}
824
825- (BOOL) privateTorrent
826{
827    return TR_FLAG_PRIVATE & fInfo->flags;
828}
829
830- (NSString *) torrentLocation
831{
832    return [NSString stringWithUTF8String: fInfo->torrent];
833}
834
835- (NSString *) publicTorrentLocation
836{
837    return fPublicTorrentLocation;
838}
839
840- (NSString *) dataLocation
841{
842    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
843}
844
845- (BOOL) publicTorrent
846{
847    return fPublicTorrent;
848}
849
850- (NSString *) stateString
851{
852    switch( fStat->status )
853    {
854        case TR_STATUS_PAUSE:
855            return NSLocalizedString(@"Paused", "Torrent -> status string");
856            break;
857
858        case TR_STATUS_CHECK:
859            return [NSLocalizedString(@"Checking existing files", "Torrent -> status string") stringByAppendingEllipsis];
860            break;
861
862        case TR_STATUS_DOWNLOAD:
863            return NSLocalizedString(@"Downloading", "Torrent -> status string");
864            break;
865
866        case TR_STATUS_SEED:
867            return NSLocalizedString(@"Seeding", "Torrent -> status string");
868            break;
869
870        case TR_STATUS_STOPPING:
871            return [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
872            break;
873       
874        default:
875            return NSLocalizedString(@"N/A", "Torrent -> status string");
876    }
877}
878
879- (float) progress
880{
881    return fStat->progress;
882}
883
884- (int) eta
885{
886    return fStat->eta;
887}
888
889- (BOOL) isActive
890{
891    return fStat->status & TR_STATUS_ACTIVE;
892}
893
894- (BOOL) isSeeding
895{
896    return fStat->status == TR_STATUS_SEED;
897}
898
899- (BOOL) isPaused
900{
901    return fStat->status == TR_STATUS_PAUSE;
902}
903
904- (BOOL) isError
905{
906    return fStat->error;
907}
908
909- (NSString *) errorMessage
910{
911    return [self isError] ? [NSString stringWithUTF8String: fStat->errorString] : @"";
912}
913
914- (BOOL) justFinished
915{
916    return tr_getFinished(fHandle);
917}
918
919- (NSArray *) peers
920{
921    int totalPeers, i;
922    tr_peer_stat_t * peers = tr_torrentPeers(fHandle, & totalPeers);
923   
924    NSMutableArray * peerDics = [NSMutableArray arrayWithCapacity: totalPeers];
925    NSMutableDictionary * dic;
926   
927    tr_peer_stat_t * peer;
928    NSString * client;
929    for (i = 0; i < totalPeers; i++)
930    {
931        peer = &peers[i];
932       
933        dic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
934            [NSNumber numberWithBool: peer->isConnected], @"Connected",
935            [NSNumber numberWithBool: peer->isIncoming], @"Incoming",
936            [NSString stringWithCString: (char *) peer->addr encoding: NSUTF8StringEncoding], @"IP",
937            [NSString stringWithCString: (char *) peer->client encoding: NSUTF8StringEncoding], @"Client",
938            [NSNumber numberWithFloat: peer->progress], @"Progress",
939            [NSNumber numberWithBool: peer->isDownloading], @"UL To",
940            [NSNumber numberWithBool: peer->isUploading], @"DL From",
941            [NSNumber numberWithInt: peer->port], @"Port", nil];
942       
943        if (peer->isDownloading)
944            [dic setObject: [NSNumber numberWithFloat: peer->uploadToRate] forKey: @"UL To Rate"];
945        if (peer->isUploading)
946            [dic setObject: [NSNumber numberWithFloat: peer->downloadFromRate] forKey: @"DL From Rate"];
947       
948        [peerDics addObject: dic];
949    }
950   
951    tr_torrentPeersFree(peers, totalPeers);
952   
953    return peerDics;
954}
955
956- (NSString *) progressString
957{
958    return fProgressString;
959}
960
961- (NSString *) statusString
962{
963    return fStatusString;
964}
965
966- (NSString *) shortStatusString
967{
968    return fShortStatusString;
969}
970
971- (NSString *) remainingTimeString
972{
973    return fRemainingTimeString;
974}
975
976- (int) seeders
977{
978    return fStat->seeders;
979}
980
981- (int) leechers
982{
983    return fStat->leechers;
984}
985
986- (int) completedFromTracker
987{
988    return fStat->completedFromTracker;
989}
990
991- (int) totalPeers
992{
993    return fStat->peersTotal;
994}
995
996- (int) totalPeersIncoming
997{
998    return fStat->peersIncoming;
999}
1000
1001- (int) totalPeersOutgoing
1002{
1003    return [self totalPeers] - [self totalPeersIncoming];
1004}
1005
1006//peers uploading to you
1007- (int) peersUploading
1008{
1009    return fStat->peersUploading;
1010}
1011
1012//peers downloading from you
1013- (int) peersDownloading
1014{
1015    return fStat->peersDownloading;
1016}
1017
1018- (float) downloadRate
1019{
1020    return fStat->rateDownload;
1021}
1022
1023- (float) uploadRate
1024{
1025    return fStat->rateUpload;
1026}
1027
1028- (float) downloadedValid
1029{
1030    return [self progress] * [self size];
1031}
1032
1033- (uint64_t) downloadedTotal
1034{
1035    return fStat->downloaded;
1036}
1037
1038- (uint64_t) uploadedTotal
1039{
1040    return fStat->uploaded;
1041}
1042
1043- (float) swarmSpeed
1044{
1045    return fStat->swarmspeed;
1046}
1047
1048- (NSNumber *) orderValue
1049{
1050    return [NSNumber numberWithInt: fOrderValue];
1051}
1052
1053- (void) setOrderValue: (int) orderValue
1054{
1055    fOrderValue = orderValue;
1056}
1057
1058- (NSArray *) fileList
1059{
1060    return fFileList;
1061}
1062
1063- (int) fileCount
1064{
1065    return fInfo->fileCount;
1066}
1067
1068- (BOOL) updateFileProgress
1069{
1070    BOOL change = NO;
1071    float * progress = tr_torrentCompletion(fHandle);
1072    NSNumber * progressNum;
1073    NSMutableDictionary * item, * dict;
1074   
1075    int i, fileCount = [self fileCount];
1076    for (i = 0; i < fileCount; i++)
1077    {
1078        if (!(progressNum = [[fFileFlatList objectAtIndex: i] objectForKey: @"Progress"])
1079                || [progressNum floatValue] != progress[i])
1080        {
1081            item = [fFileFlatList objectAtIndex: i];
1082            [item setObject: [NSNumber numberWithFloat: progress[i]] forKey: @"Progress"];
1083            change = YES;
1084           
1085            if (progress[i] == 1.0)
1086            {
1087                dict = item;
1088                while ((dict = [dict objectForKey: @"Parent"]))
1089                    [dict setObject: [NSNumber numberWithInt: [[dict objectForKey: @"Remaining"] intValue]-1]
1090                            forKey: @"Remaining"];
1091               
1092                [item setObject: [NSNumber numberWithInt: NSOnState] forKey: @"Check"];
1093                [self resetFileCheckStateForItemParent: item];
1094            }
1095        }
1096    }
1097   
1098    free(progress);
1099   
1100    return change;
1101}
1102
1103- (void) setFileCheckState: (int) state forFileItem: (NSMutableDictionary *) item
1104{
1105    [item setObject: [NSNumber numberWithInt: state] forKey: @"Check"];
1106   
1107    if (![[item objectForKey: @"IsFolder"] boolValue])
1108        return;
1109   
1110    NSMutableDictionary * child;
1111    NSEnumerator * enumerator = [[item objectForKey: @"Children"] objectEnumerator];
1112    while ((child = [enumerator nextObject]))
1113        if (state != [[child objectForKey: @"Check"] intValue])
1114            [self setFileCheckState: state forFileItem: child];
1115}
1116
1117- (NSMutableDictionary *) resetFileCheckStateForItemParent: (NSMutableDictionary *) originalChild
1118{
1119    NSMutableDictionary * item;
1120    if (!(item = [originalChild objectForKey: @"Parent"]))
1121        return originalChild;
1122   
1123    int state = INVALID;
1124   
1125    NSMutableDictionary * child;
1126    NSEnumerator * enumerator = [[item objectForKey: @"Children"] objectEnumerator];
1127    while ((child = [enumerator nextObject]))
1128    {
1129        if (state == INVALID)
1130        {
1131            state = [[child objectForKey: @"Check"] intValue];
1132            if (state == NSMixedState)
1133                break;
1134        }
1135        else if (state != [[child objectForKey: @"Check"] intValue])
1136        {
1137            state = NSMixedState;
1138            break;
1139        }
1140        else;
1141    }
1142   
1143    if (state != [[item objectForKey: @"Check"] intValue])
1144    {
1145        [item setObject: [NSNumber numberWithInt: state] forKey: @"Check"];
1146        return [self resetFileCheckStateForItemParent: item];
1147    }
1148    else
1149        return originalChild;
1150}
1151
1152- (NSDate *) date
1153{
1154    return fDate;
1155}
1156
1157- (NSNumber *) stateSortKey
1158{
1159    if (![self isActive])
1160        return [NSNumber numberWithInt: 0];
1161    else if ([self isSeeding])
1162        return [NSNumber numberWithInt: 1];
1163    else
1164        return [NSNumber numberWithInt: 2];
1165}
1166
1167- (NSNumber *) progressSortKey
1168{
1169    return [NSNumber numberWithFloat: [self progress]];
1170}
1171
1172- (NSNumber *) ratioSortKey
1173{
1174    float ratio = [self ratio];
1175    if (ratio == TR_RATIO_INF)
1176        return [NSNumber numberWithInt: 999999999]; //this should hopefully be big enough
1177    else
1178        return [NSNumber numberWithFloat: [self ratio]];
1179}
1180
1181@end
1182
1183@implementation Torrent (Private)
1184
1185//if a hash is given, attempt to load that; otherwise, attempt to open file at path
1186- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
1187        publicTorrent: (NSNumber *) publicTorrent
1188        date: (NSDate *) date
1189        ratioSetting: (NSNumber *) ratioSetting ratioLimit: (NSNumber *) ratioLimit
1190        limitSpeedCustom: (NSNumber *) limitCustom
1191        checkUpload: (NSNumber *) checkUpload uploadLimit: (NSNumber *) uploadLimit
1192        checkDownload: (NSNumber *) checkDownload downloadLimit: (NSNumber *) downloadLimit
1193        waitToStart: (NSNumber *) waitToStart orderValue: (NSNumber *) orderValue
1194{
1195    if (!(self = [super init]))
1196        return nil;
1197
1198    fLib = lib;
1199    fDefaults = [NSUserDefaults standardUserDefaults];
1200
1201    fPublicTorrent = path && (publicTorrent ? [publicTorrent boolValue] : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
1202    if (fPublicTorrent)
1203        fPublicTorrentLocation = [path retain];
1204
1205    int error;
1206    if (hashString)
1207        fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], TR_FLAG_SAVE, & error);
1208   
1209    if (!fHandle && path)
1210        fHandle = tr_torrentInit(fLib, [path UTF8String], TR_FLAG_SAVE, & error);
1211
1212    if (!fHandle)
1213    {
1214        [self release];
1215        return nil;
1216    }
1217   
1218    NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
1219    [nc addObserver: self selector: @selector(updateSpeedSetting:)
1220                name: @"UpdateSpeedSetting" object: nil];
1221   
1222    fInfo = tr_torrentInfo(fHandle);
1223
1224    fDate = date ? [date retain] : [[NSDate alloc] init];
1225   
1226    fRatioSetting = ratioSetting ? [ratioSetting intValue] : NSMixedState;
1227    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
1228    fFinishedSeeding = NO;
1229   
1230    fCheckUpload = checkUpload ? [checkUpload intValue] : NSMixedState;
1231    fUploadLimit = uploadLimit ? [uploadLimit intValue] : [fDefaults integerForKey: @"UploadLimit"];
1232    fCheckDownload = checkDownload ? [checkDownload intValue] : NSMixedState;
1233    fDownloadLimit = downloadLimit ? [downloadLimit intValue] : [fDefaults integerForKey: @"DownloadLimit"];
1234    [self updateSpeedSetting];
1235   
1236    fWaitToStart = waitToStart ? [waitToStart boolValue] : [fDefaults boolForKey: @"AutoStartDownload"];
1237    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
1238    fError = NO;
1239   
1240    fIcon = [[[NSWorkspace sharedWorkspace] iconForFileType: fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr')
1241                                                : [[self name] pathExtension]] retain];
1242   
1243    fIconFlipped = [fIcon copy];
1244    [fIconFlipped setFlipped: YES];
1245   
1246    fIconSmall = [fIconFlipped copy];
1247    [fIconSmall setScalesWhenResized: YES];
1248    [fIconSmall setSize: NSMakeSize(16.0, 16.0)];
1249
1250    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
1251    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
1252    fShortStatusString = [[NSMutableString alloc] initWithCapacity: 30];
1253    fRemainingTimeString = [[NSMutableString alloc] initWithCapacity: 30];
1254   
1255    [self createFileList];
1256   
1257    //set up advanced bar
1258    fBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
1259        pixelsWide: MAX_PIECES pixelsHigh: BAR_HEIGHT bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
1260        isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
1261   
1262    fPieces = malloc(MAX_PIECES);
1263    int i;
1264    for (i = 0; i < MAX_PIECES; i++)
1265        fPieces[i] = BLANK_PIECE;
1266
1267    [self update];
1268    return self;
1269}
1270
1271- (void) createFileList
1272{
1273    int count = [self fileCount], i;
1274    tr_file_t * file;
1275    NSMutableArray * pathComponents;
1276    NSString * path;
1277   
1278    NSMutableArray * fileList = [[NSMutableArray alloc] init],
1279                    * fileFlatList = [[NSMutableArray alloc] initWithCapacity: count];
1280   
1281    for (i = 0; i < count; i++)
1282    {
1283        file = &fInfo->files[i];
1284       
1285        pathComponents = [[[NSString stringWithUTF8String: file->name] pathComponents] mutableCopy];
1286        if (fInfo->multifile)
1287        {
1288            path = [pathComponents objectAtIndex: 0];
1289            [pathComponents removeObjectAtIndex: 0];
1290        }
1291        else
1292            path = @"";
1293       
1294        [self insertPath: pathComponents forSiblings: fileList withParent: nil previousPath: path
1295                fileSize: file->length state: NSOnState flatList: fileFlatList];
1296        [pathComponents autorelease];
1297    }
1298   
1299    fFileList = [[NSArray alloc] initWithArray: fileList];
1300    [fileList release];
1301    fFileFlatList = [[NSArray alloc] initWithArray: fileFlatList];
1302    [fileFlatList release];
1303}
1304
1305- (void) insertPath: (NSMutableArray *) components forSiblings: (NSMutableArray *) siblings
1306        withParent: (NSMutableDictionary *) parent previousPath: (NSString *) previousPath
1307        fileSize: (uint64_t) size state: (int) state flatList: (NSMutableArray *) flatList
1308{
1309    NSString * name = [components objectAtIndex: 0];
1310    BOOL isFolder = [components count] > 1;
1311   
1312    NSMutableDictionary * dict = nil;
1313    if (isFolder)
1314    {
1315        NSEnumerator * enumerator = [siblings objectEnumerator];
1316        while ((dict = [enumerator nextObject]))
1317            if ([[dict objectForKey: @"Name"] isEqualToString: name] && [[dict objectForKey: @"IsFolder"] boolValue])
1318                break;
1319    }
1320   
1321    NSString * currentPath = [previousPath stringByAppendingPathComponent: name];
1322   
1323    //create new folder or item if it doesn't already exist
1324    if (!dict)
1325    {
1326        dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: name, @"Name",
1327                                [NSNumber numberWithBool: isFolder], @"IsFolder",
1328                                currentPath, @"Path", nil];
1329       
1330        [siblings addObject: dict];
1331       
1332        if (isFolder)
1333        {
1334            [dict setObject: [NSMutableArray array] forKey: @"Children"];
1335            [dict setObject: [NSNumber numberWithInt: 1] forKey: @"Remaining"];
1336        }
1337        else
1338        {
1339            [flatList addObject: dict];
1340            [dict setObject: [NSNumber numberWithUnsignedLongLong: size] forKey: @"Size"];
1341            [dict setObject: [[NSWorkspace sharedWorkspace] iconForFileType: [name pathExtension]] forKey: @"Icon"];
1342        }
1343       
1344        if (parent)
1345            [dict setObject: parent forKey: @"Parent"];
1346        [dict setObject: [NSNumber numberWithInt: state] forKey: @"Check"];
1347    }
1348    else
1349    {
1350        if (isFolder)
1351            [dict setObject: [NSNumber numberWithInt: [[dict objectForKey: @"Remaining"] intValue]+1] forKey: @"Remaining"];
1352       
1353        int dictState = [[dict objectForKey: @"Check"] intValue];
1354        if (dictState != NSMixedState && dictState != state)
1355            [dict setObject: [NSNumber numberWithInt: NSMixedState] forKey: @"Check"];
1356    }
1357   
1358    if (isFolder)
1359    {
1360        [components removeObjectAtIndex: 0];
1361        [self insertPath: components forSiblings: [dict objectForKey: @"Children"]
1362            withParent: dict previousPath: currentPath fileSize: size state: state flatList: flatList];
1363    }
1364}
1365
1366- (NSImage *) advancedBar
1367{
1368    uint32_t * p;
1369    uint8_t * bitmapData = [fBitmap bitmapData];
1370    int bytesPerRow = [fBitmap bytesPerRow];
1371   
1372    int pieceCount = [self pieceCount];
1373    int8_t * piecesAvailablity = malloc(pieceCount);
1374    [self getAvailability: piecesAvailablity size: pieceCount];
1375   
1376    //lines 2 to 14: blue, green, or gray depending on whether we have the piece or not
1377    int i, h, index = 0;
1378    float increment = (float)pieceCount / (float)MAX_PIECES, indexValue = 0;
1379    uint32_t color;
1380    BOOL change;
1381    for (i = 0; i < MAX_PIECES; i++)
1382    {
1383        change = NO;
1384        if (piecesAvailablity[index] < 0)
1385        {
1386            if (fPieces[i] != -1)
1387            {
1388                color = kBlue;
1389                fPieces[i] = -1;
1390                change = YES;
1391            }
1392        }
1393        else if (piecesAvailablity[index] == 0)
1394        {
1395            if (fPieces[i] != 0)
1396            {
1397                color = kGray;
1398                fPieces[i] = 0;
1399                change = YES;
1400            }
1401        }
1402        else
1403        {
1404            if (piecesAvailablity[index] == 1)
1405            {
1406                if (fPieces[i] != 1)
1407                {
1408                    color = kGreen1;
1409                    fPieces[i] = 1;
1410                    change = YES;
1411                }
1412            }
1413            else if (piecesAvailablity[index] == 2)
1414            {
1415                if (fPieces[i] != 2)
1416                {
1417                    color = kGreen2;
1418                    fPieces[i] = 2;
1419                    change = YES;
1420                }
1421            }
1422            else
1423            {
1424                if (fPieces[i] != 3)
1425                {
1426                    color = kGreen3;
1427                    fPieces[i] = 3;
1428                    change = YES;
1429                }
1430            }
1431        }
1432       
1433        if (change)
1434        {
1435            //point to pixel (i, 2) and draw "vertically"
1436            p = (uint32_t *)(bitmapData + 2 * bytesPerRow) + i;
1437            for (h = 2; h < BAR_HEIGHT; h++)
1438            {
1439                p[0] = color;
1440                p = (uint32_t *)((uint8_t *)p + bytesPerRow);
1441            }
1442        }
1443       
1444        indexValue += increment;
1445        index = (int)indexValue;
1446    }
1447   
1448    //determine percentage finished and available
1449    int have = rintf((float)MAX_PIECES * [self progress]), avail;
1450    if ([self progress] >= 1.0 || ![self isActive] || [self totalPeers] <= 0)
1451        avail = 0;
1452    else
1453    {
1454        float * piecesFinished = malloc(pieceCount * sizeof(float));
1455        [self getAmountFinished: piecesFinished size: pieceCount];
1456       
1457        float available = 0;
1458        for (i = 0; i < pieceCount; i++)
1459            if (piecesAvailablity[i] > 0)
1460                available += 1.0 - piecesFinished[i];
1461       
1462        avail = rintf((float)MAX_PIECES * available / (float)pieceCount);
1463        if (have + avail > MAX_PIECES) //case if both end in .5 and all pieces are available
1464            avail--;
1465       
1466        free(piecesFinished);
1467    }
1468   
1469    free(piecesAvailablity);
1470   
1471    //first two lines: dark blue to show progression, green to show available
1472    p = (uint32_t *)bitmapData;
1473    for (i = 0; i < have; i++)
1474    {
1475        p[i] = kBlue2;
1476        p[i + bytesPerRow / 4] = kBlue2;
1477    }
1478    for (; i < avail + have; i++)
1479    {
1480        p[i] = kGreen3;
1481        p[i + bytesPerRow / 4] = kGreen3;
1482    }
1483    for (; i < MAX_PIECES; i++)
1484    {
1485        p[i] = kWhite;
1486        p[i + bytesPerRow / 4] = kWhite;
1487    }
1488   
1489    //actually draw image
1490    NSImage * bar = [[NSImage alloc] initWithSize: [fBitmap size]];
1491    [bar addRepresentation: fBitmap];
1492    [bar setScalesWhenResized: YES];
1493   
1494    return [bar autorelease];
1495}
1496
1497- (void) trashFile: (NSString *) path
1498{
1499    //attempt to move to trash
1500    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
1501            source: [path stringByDeletingLastPathComponent] destination: @""
1502            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
1503    {
1504        //if cannot trash, just delete it (will work if it is on a remote volume)
1505        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
1506            NSLog(@"Could not trash %@", path);
1507    }
1508}
1509
1510@end
Note: See TracBrowser for help on using the repository browser.