source: trunk/macosx/Torrent.m @ 1611

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

store date completed internally

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