source: trunk/macosx/Torrent.m @ 1609

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

rename date to date added

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