source: trunk/macosx/Torrent.m @ 1756

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

expand incomplete folder path

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