source: trunk/macosx/Torrent.m @ 272

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

First commit on my own 8-)

Sorting by progress, sorting no longer causes crashes.
New look for the table.
Info is now inspector: can set individual and multiple ratio limits, shows listing of files, more info in general, resizes when changing tabs, can change tabs with cmd-left and cmd-right.
Menu items moved to "Transfers" menu.
Sliding status bar in its own view.
Prefs moved into their own nib.
Toolbar items for pause and resume selected (many wanted this, but it needs better icons)
New icons for Transfers and General.
A lot of tweaking of main window to fix alignment etc.
Sparkle used for updated (still needs to be added to website to work).

And a lot more tweaking and changes that I'm too lazy to list.

...now let's hope I commit this right

  • Property svn:keywords set to Date Rev Author Id
File size: 13.3 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 272 2006-06-06 18:05:57Z 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#import "Utils.h"
28
29@interface Torrent (Private)
30
31- (void) trashPath: (NSString *) path;
32- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib date: (NSDate *) date
33        stopRatioSetting: (NSNumber *) stopRatioSetting ratioLimit: (NSNumber *) ratioLimit;
34
35@end
36
37
38@implementation Torrent
39
40- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib
41{
42    return [self initWithPath: path lib: lib
43            date: nil stopRatioSetting: nil
44            ratioLimit: nil];
45}
46
47- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle_t *) lib
48{
49    self = [self initWithPath: [history objectForKey: @"TorrentPath"]
50            lib: lib date: [history objectForKey: @"Date"]
51            stopRatioSetting: [history objectForKey: @"StopRatioSetting"]
52            ratioLimit: [history objectForKey: @"RatioLimit"]];
53           
54    if (self)
55    {
56        NSString * downloadFolder;
57        if (!(downloadFolder = [history objectForKey: @"DownloadFolder"]))
58            downloadFolder = [[fDefaults stringForKey: @"DownloadFolder"]
59                                stringByExpandingTildeInPath];
60        [self setDownloadFolder: downloadFolder];
61
62        NSString * paused;
63        if (!(paused = [history objectForKey: @"Paused"]) || [paused isEqualToString: @"NO"])
64            [self start];
65    }
66    return self;
67}
68
69- (NSDictionary *) history
70{
71    return [NSDictionary dictionaryWithObjectsAndKeys:
72            [self torrentLocation], @"TorrentPath",
73            [self downloadFolder], @"DownloadFolder",
74            [self isActive] ? @"NO" : @"YES", @"Paused",
75            [self date], @"Date",
76            [NSNumber numberWithInt: fStopRatioSetting], @"StopRatioSetting",
77            [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit", nil];
78}
79
80- (void) dealloc
81{
82    if( fHandle )
83    {
84        tr_torrentClose( fLib, fHandle );
85       
86        [fDate release];
87        [fIcon release];
88        [fIconFlipped release];
89        [fProgressString release];
90        [fStatusString release];
91    }
92    [super dealloc];
93}
94
95- (void) setDownloadFolder: (NSString *) path
96{
97    tr_torrentSetFolder( fHandle, [path UTF8String] );
98}
99
100- (NSString *) downloadFolder
101{
102    return [NSString stringWithUTF8String: tr_torrentGetFolder( fHandle )];
103}
104
105- (void) getAvailability: (int8_t *) tab size: (int) size
106{
107    tr_torrentAvailability( fHandle, tab, size );
108}
109
110- (void) update
111{
112    fStat = tr_torrentStat( fHandle );
113   
114    if ([self isSeeding])
115        if ((fStopRatioSetting == RATIO_CHECK && [self ratio] >= fRatioLimit)
116            || (fStopRatioSetting == RATIO_GLOBAL && [fDefaults boolForKey: @"RatioCheck"]
117                && [self ratio] >= [fDefaults floatForKey: @"RatioLimit"]))
118        {
119            [self stop];
120            [self setStopRatioSetting: RATIO_NO_CHECK];
121           
122            fStat = tr_torrentStat( fHandle );
123           
124            [[NSNotificationCenter defaultCenter] postNotificationName:
125                @"TorrentRatioChanged" object: self];
126        }
127   
128    [fProgressString setString: @""];
129    if ([self progress] < 1.0)
130        [fProgressString appendFormat: @"%@ of %@ completed (%.2f%%)", [NSString stringForFileSize:
131                [self downloaded]], [NSString stringForFileSize: [self size]], 100 * [self progress]];
132    else
133        [fProgressString appendFormat: @"%@, uploaded %@ (ratio: %@)", [NSString stringForFileSize:
134                [self size]], [NSString stringForFileSize: [self uploaded]],
135                [NSString stringForRatioWithDownload: [self downloaded] upload: [self uploaded]]];
136
137    switch( fStat->status )
138    {
139        case TR_STATUS_PAUSE:
140            [fStatusString setString: @"Paused"];
141            break;
142
143        case TR_STATUS_CHECK:
144            [fStatusString setString: [@"Checking existing files" stringByAppendingString: NS_ELLIPSIS]];
145            break;
146
147        case TR_STATUS_DOWNLOAD:
148            [fStatusString setString: @""];
149            [fStatusString appendFormat:
150                @"Downloading from %d of %d peer%s",
151                [self peersUploading], [self totalPeers],
152                ([self totalPeers] == 1) ? "" : "s"];
153           
154            int eta = fStat->eta;
155            if (eta < 0)
156                [fProgressString appendString: @" - remaining time unknown"];
157            else
158            {
159                if (eta < 60)
160                    [fProgressString appendFormat: @" - %d sec remaining", eta];
161                else if (eta < 3600)
162                    [fProgressString appendFormat: @" - %d min %02d sec remaining",
163                                                    eta / 60, eta % 60];
164                else
165                    [fProgressString appendFormat: @" - %d hr %02d min remaining",
166                                                    eta / 3600, (eta / 60) % 60];
167            }
168            break;
169
170        case TR_STATUS_SEED:
171            [fStatusString setString: @""];
172            [fStatusString appendFormat:
173                @"Seeding to %d of %d peer%s",
174                [self peersDownloading], [self totalPeers],
175                ([self totalPeers] == 1) ? "" : "s"];
176            break;
177
178        case TR_STATUS_STOPPING:
179            [fStatusString setString: [@"Stopping" stringByAppendingString: NS_ELLIPSIS]];
180            break;
181    }
182   
183    if( fStat->error & TR_ETRACKER )
184    {
185        [fStatusString setString: [@"Error: " stringByAppendingString:
186            [NSString stringWithUTF8String: fStat->trackerError]]];
187    }
188
189    if ([self isActive])
190    {
191        if ([self progress] < 1.0)
192            [fStatusString appendFormat: @" - DL: %@, ", [NSString stringForSpeed: [self downloadRate]]];
193        [fStatusString appendString: [@"UL: " stringByAppendingString:
194                                                [NSString stringForSpeed: [self uploadRate]]]];
195    }
196}
197
198- (void) start
199{
200    if( fStat->status & TR_STATUS_INACTIVE )
201    {
202        tr_torrentStart( fHandle );
203    }
204}
205
206- (void) stop
207{
208    if( fStat->status & TR_STATUS_ACTIVE )
209    {
210        tr_torrentStop( fHandle );
211    }
212}
213
214- (void) sleep
215{
216    if( ( fResumeOnWake = ( fStat->status & TR_STATUS_ACTIVE ) ) )
217    {
218        [self stop];
219    }
220}
221
222- (void) wakeUp
223{
224    if( fResumeOnWake )
225    {
226        [self start];
227    }
228}
229
230- (float) ratio
231{
232    float downloaded = [self downloaded];
233    return downloaded > 0 ? (float)[self uploaded] / downloaded : -1;
234}
235
236- (int) stopRatioSetting
237{
238        return fStopRatioSetting;
239}
240
241- (void) setStopRatioSetting: (int) setting
242{
243    fStopRatioSetting = setting;
244}
245
246- (float) ratioLimit
247{
248    return fRatioLimit;
249}
250
251- (void) setRatioLimit: (float) limit
252{
253    if (limit >= 0)
254        fRatioLimit = limit;
255}
256
257- (void) reveal
258{
259    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation]
260        inFileViewerRootedAtPath: nil];
261}
262
263- (void) trashTorrent
264{
265    [self trashPath: [self torrentLocation]];
266}
267
268- (void) trashData
269{
270    [self trashPath: [self dataLocation]];
271}
272
273- (NSImage *) icon
274{
275    return fIcon;
276}
277
278- (NSImage *) iconFlipped
279{
280    return fIconFlipped;
281}
282
283- (NSString *) name
284{
285    return [NSString stringWithUTF8String: fInfo->name];
286}
287
288- (uint64_t) size
289{
290    return fInfo->totalSize;
291}
292
293- (NSString *) tracker
294{
295    return [NSString stringWithFormat: @"%s:%d",
296            fInfo->trackerAddress, fInfo->trackerPort];
297}
298
299- (NSString *) announce
300{
301    return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
302}
303
304- (int) pieceSize
305{
306    return fInfo->pieceSize;
307}
308
309- (int) pieceCount
310{
311    return fInfo->pieceCount;
312}
313
314- (NSString *) hashString
315{
316    NSMutableString * string = [NSMutableString
317        stringWithCapacity: SHA_DIGEST_LENGTH];
318    int i;
319    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
320    {
321        [string appendFormat: @"%02x", fInfo->hash[i]];
322    }
323    return string;
324}
325
326- (NSString *) torrentLocation
327{
328    return [NSString stringWithUTF8String: fInfo->torrent];;
329}
330
331- (NSString *) dataLocation
332{
333    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
334}
335
336- (NSString *) state
337{
338    switch( fStat->status )
339    {
340        case TR_STATUS_PAUSE:
341            return @"Paused";
342            break;
343
344        case TR_STATUS_CHECK:
345            return [@"Checking existing files" stringByAppendingString: NS_ELLIPSIS];
346            break;
347
348        case TR_STATUS_DOWNLOAD:
349            return @"Downloading";
350            break;
351
352        case TR_STATUS_SEED:
353            return @"Seeding";
354            break;
355
356        case TR_STATUS_STOPPING:
357            return [@"Stopping" stringByAppendingString: NS_ELLIPSIS];
358            break;
359       
360        default:
361            return @"N/A";
362    }
363}
364
365- (float) progress
366{
367    return fStat->progress;
368}
369
370- (BOOL) isActive
371{
372    return ( fStat->status & TR_STATUS_ACTIVE );
373}
374
375- (BOOL) isSeeding
376{
377    return ( fStat->status == TR_STATUS_SEED );
378}
379
380- (BOOL) isPaused
381{
382    return ( fStat->status == TR_STATUS_PAUSE );
383}
384
385- (BOOL) justFinished
386{
387    return tr_getFinished( fHandle );
388}
389
390- (NSString *) progressString
391{
392    return fProgressString;
393}
394
395- (NSString *) statusString
396{
397    return fStatusString;
398}
399
400- (int) seeders
401{
402    return fStat->seeders;
403}
404
405- (int) leechers
406{
407    return fStat->leechers;
408}
409
410- (int) totalPeers
411{
412    return fStat->peersTotal;
413}
414
415//peers uploading to you
416- (int) peersUploading
417{
418    return fStat->peersUploading;
419}
420
421//peers downloading from you
422- (int) peersDownloading
423{
424    return fStat->peersDownloading;
425}
426
427- (float) downloadRate
428{
429    return fStat->rateDownload;
430}
431
432- (float) uploadRate
433{
434    return fStat->rateUpload;
435}
436
437- (uint64_t) downloaded
438{
439    return fStat->downloaded;
440}
441
442- (uint64_t) uploaded
443{
444    return fStat->uploaded;
445}
446
447- (NSArray *) fileList
448{
449    int count = fInfo->fileCount, i;
450    NSMutableArray * files = [NSMutableArray arrayWithCapacity: count];
451    for (i = 0; i < count; i++)
452        [files addObject: [[self downloadFolder] stringByAppendingPathComponent:
453            [NSString stringWithUTF8String: fInfo->files[i].name]]];
454    return files;
455}
456
457- (NSDate *) date
458{
459    return fDate;
460}
461
462- (NSNumber *) stateSortKey
463{
464    if (fStat->status & TR_STATUS_INACTIVE)
465        return [NSNumber numberWithInt: 0];
466    else if (fStat->status == TR_STATUS_SEED)
467        return [NSNumber numberWithInt: 1];
468    else
469        return [NSNumber numberWithInt: 2];
470}
471
472- (NSNumber *) progressSortKey
473{
474    return [NSNumber numberWithFloat: [self progress]];
475}
476
477@end
478
479
480@implementation Torrent (Private)
481
482- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib date: (NSDate *) date
483        stopRatioSetting: (NSNumber *) stopRatioSetting ratioLimit: (NSNumber *) ratioLimit
484{
485    if (!(self = [super init]))
486        return nil;
487
488    fLib = lib;
489
490    int error;
491    if (!path || !(fHandle = tr_torrentInit(fLib, [path UTF8String], &error)))
492    {
493        [self release];
494        return nil;
495    }
496   
497    fInfo = tr_torrentInfo( fHandle );
498   
499    fDefaults = [NSUserDefaults standardUserDefaults];
500
501    fDate = date ? [date retain] : [[NSDate alloc] init];
502    fStopRatioSetting = stopRatioSetting ? [stopRatioSetting intValue] : -1;
503    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
504   
505    NSString * fileType = ( fInfo->fileCount > 1 ) ?
506        NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
507    fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
508    [fIcon retain];
509   
510    fIconFlipped = [fIcon copy];
511    [fIconFlipped setFlipped: YES];
512
513    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
514    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
515
516    [self update];
517    return self;
518}
519
520- (void) trashPath: (NSString *) path
521{
522    if( ![[NSWorkspace sharedWorkspace] performFileOperation:
523           NSWorkspaceRecycleOperation source:
524           [path stringByDeletingLastPathComponent]
525           destination: @""
526           files: [NSArray arrayWithObject: [path lastPathComponent]]
527           tag: nil] )
528    {
529        /* We can't move it to the trash, let's try just to delete it
530           (will work if it is on a remote volume) */
531        if( ![[NSFileManager defaultManager]
532                removeFileAtPath: path handler: nil] )
533        {
534            NSLog( [@"Could not trash " stringByAppendingString: path] );
535        }
536    }
537}
538
539@end
Note: See TracBrowser for help on using the repository browser.