source: trunk/macosx/InfoWindowController.m @ 2812

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

don't resize the inspector unnecessarily

  • Property svn:keywords set to Date Rev Author Id
File size: 44.9 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 2812 2007-08-17 01:29:46Z 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 "InfoWindowController.h"
26#import "StringAdditions.h"
27
28#define FILE_ROW_SMALL_HEIGHT 18.0
29
30#define TAB_INFO_IDENT @"Info"
31#define TAB_ACTIVITY_IDENT @"Activity"
32#define TAB_PEERS_IDENT @"Peers"
33#define TAB_FILES_IDENT @"Files"
34#define TAB_OPTIONS_IDENT @"Options"
35
36//15 spacing at the bottom of each tab
37#define TAB_INFO_HEIGHT 268.0
38#define TAB_ACTIVITY_HEIGHT 274.0
39#define TAB_PEERS_HEIGHT 279.0
40#define TAB_FILES_HEIGHT 279.0
41#define TAB_OPTIONS_HEIGHT 158.0
42
43#define PIECES_CONTROL_PROGRESS 0
44#define PIECES_CONTROL_AVAILABLE 1
45
46#define OPTION_POPUP_GLOBAL 0
47#define OPTION_POPUP_NO_LIMIT 1
48#define OPTION_POPUP_LIMIT 2
49
50#define INVALID -99
51
52@interface InfoWindowController (Private)
53
54- (void) updateInfoGeneral;
55- (void) updateInfoActivity;
56- (void) updateInfoPeers;
57- (void) updateInfoFiles;
58- (void) updateInfoSettings;
59
60- (void) setWindowForTab: (NSString *) identifier animate: (BOOL) animate;
61- (NSArray *) peerSortDescriptors;
62
63@end
64
65@implementation InfoWindowController
66
67- (id) initWithWindowNibName: (NSString *) name
68{
69    if ((self = [super initWithWindowNibName: name]))
70    {
71        fAppIcon = [NSImage imageNamed: @"NSApplicationIcon"];
72        fDotGreen = [NSImage imageNamed: @"GreenDot.tiff"];
73        fDotRed = [NSImage imageNamed: @"RedDot.tiff"];
74       
75        fCanResizeVertical = NO;
76    }
77    return self;
78}
79
80- (void) awakeFromNib
81{
82    //window location and size
83    NSPanel * window = (NSPanel *)[self window];
84   
85    [window setFrameAutosaveName: @"InspectorWindowFrame"];
86    [window setFrameUsingName: @"InspectorWindowFrame"];
87   
88    //select tab
89    NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"];
90    if ([fTabView indexOfTabViewItemWithIdentifier: identifier] == NSNotFound)
91        identifier = TAB_INFO_IDENT;
92   
93    [fTabView selectTabViewItemWithIdentifier: identifier];
94    [self setWindowForTab: identifier animate: NO];
95   
96    //initially sort peer table by IP
97    if ([[fPeerTable sortDescriptors] count] == 0)
98        [fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
99                                            sortDescriptorPrototype]]];
100   
101    #warning make button? or just cell?
102    //set file table
103    [fFileOutline setDoubleAction: @selector(revealFile:)];
104   
105    //set blank inspector
106    [self updateInfoForTorrents: [NSArray array]];
107   
108    //allow for update notifications
109    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateInfoStats) name: @"UpdateStats" object: nil];
110}
111
112- (void) dealloc
113{
114    [[NSNotificationCenter defaultCenter] removeObserver: self];
115   
116    [fTorrents release];
117    [fPeers release];
118    [fFiles release];
119   
120    [super dealloc];
121}
122
123- (void) updateInfoForTorrents: (NSArray *) torrents
124{
125    [fTorrents release];
126    fTorrents = [torrents retain];
127
128    int numberSelected = [fTorrents count];
129    if (numberSelected != 1)
130    {
131        if (numberSelected > 0)
132        {
133            [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d Torrents Selected",
134                                            "Inspector -> above tabs -> selected torrents"), numberSelected]];
135       
136            uint64_t size = 0;
137            NSEnumerator * enumerator = [torrents objectEnumerator];
138            Torrent * torrent;
139            while ((torrent = [enumerator nextObject]))
140                size += [torrent size];
141           
142            [fSizeField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ Total",
143                "Inspector -> above tabs -> total size (several torrents selected)"), [NSString stringForFileSize: size]]];
144        }
145        else
146        {
147            [fNameField setStringValue: NSLocalizedString(@"No Torrents Selected",
148                                                            "Inspector -> above tabs -> selected torrents")];
149            [fSizeField setStringValue: @""];
150   
151            [fDownloadedValidField setStringValue: @""];
152            [fDownloadedTotalField setStringValue: @""];
153            [fUploadedTotalField setStringValue: @""];
154        }
155       
156        [fImageView setImage: fAppIcon];
157       
158        [fNameField setToolTip: nil];
159
160        [fTrackerField setStringValue: @""];
161        [fTrackerField setToolTip: nil];
162        [fPiecesField setStringValue: @""];
163        [fHashField setStringValue: @""];
164        [fHashField setToolTip: nil];
165        [fSecureField setStringValue: @""];
166        [fCommentView setString: @""];
167       
168        [fCreatorField setStringValue: @""];
169        [fDateCreatedField setStringValue: @""];
170        [fCommentView setSelectable: NO];
171       
172        [fTorrentLocationField setStringValue: @""];
173        [fTorrentLocationField setToolTip: nil];
174        [fDataLocationField setStringValue: @""];
175        [fDataLocationField setToolTip: nil];
176       
177        [fRevealDataButton setHidden: YES];
178        [fRevealTorrentButton setHidden: YES];
179       
180        //don't allow empty fields to be selected
181        [fTrackerField setSelectable: NO];
182        [fHashField setSelectable: NO];
183        [fCreatorField setSelectable: NO];
184        [fTorrentLocationField setSelectable: NO];
185        [fDataLocationField setSelectable: NO];
186       
187        [fStateField setStringValue: @""];
188        [fProgressField setStringValue: @""];
189        [fRatioField setStringValue: @""];
190       
191        [fSeedersField setStringValue: @""];
192        [fLeechersField setStringValue: @""];
193        [fCompletedFromTrackerField setStringValue: @""];
194        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
195        [fDownloadingFromField setStringValue: @""];
196        [fUploadingToField setStringValue: @""];
197        [fSwarmSpeedField setStringValue: @""];
198        [fErrorMessageView setString: @""];
199        [fErrorMessageView setSelectable: NO];
200       
201        [fDateAddedField setStringValue: @""];
202        [fDateCompletedField setStringValue: @""];
203        [fDateActivityField setStringValue: @""];
204       
205        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE];
206        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS];
207        [fPiecesControl setEnabled: NO];
208        [fPiecesView setTorrent: nil];
209       
210        if (fPeers)
211        {
212            [fPeers release];
213            fPeers = nil;
214        }
215       
216        if (fFiles)
217        {
218            [fFiles release];
219            fFiles = nil;
220        }
221        [fFileTableStatusField setStringValue: NSLocalizedString(@"info not available",
222                                        "Inspector -> Files tab -> bottom text (number of files)")];
223    }
224    else
225    {   
226        Torrent * torrent = [fTorrents objectAtIndex: 0];
227       
228        NSImage * icon = [[torrent icon] copy];
229        [icon setFlipped: NO];
230        [fImageView setImage: icon];
231        [icon release];
232       
233        NSString * name = [torrent name];
234        [fNameField setStringValue: name];
235        [fNameField setToolTip: name];
236        [fSizeField setStringValue: [NSString stringForFileSize: [torrent size]]];
237       
238        NSString * hashString = [torrent hashString];
239        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
240                                        [NSString stringForFileSize: [torrent pieceSize]]]];
241        [fHashField setStringValue: hashString];
242        [fHashField setToolTip: hashString];
243        [fSecureField setStringValue: [torrent privateTorrent]
244                        ? NSLocalizedString(@"Private Torrent, PEX disabled", "Inspector -> is private torrent")
245                        : NSLocalizedString(@"Public Torrent", "Inspector -> is not private torrent")];
246       
247        NSString * commentString = [torrent comment];
248        [fCommentView setString: commentString];
249       
250        NSString * creatorString = [torrent creator];
251        [fCreatorField setStringValue: creatorString];
252        [fDateCreatedField setObjectValue: [torrent dateCreated]];
253       
254        BOOL publicTorrent = [torrent publicTorrent];
255        [fTorrentLocationField setStringValue: publicTorrent
256                    ? [[torrent publicTorrentLocation] stringByAbbreviatingWithTildeInPath]
257                    : NSLocalizedString(@"Transmission Support Folder", "Torrent -> location when deleting original")];
258        if (publicTorrent)
259            [fTorrentLocationField setToolTip: [NSString stringWithFormat: @"%@\n\n%@",
260                        [torrent publicTorrentLocation], [torrent torrentLocation]]];
261        else
262            [fTorrentLocationField setToolTip: [torrent torrentLocation]];
263       
264        [fDateAddedField setObjectValue: [torrent dateAdded]];
265       
266        [fRevealDataButton setHidden: NO];
267        [fRevealTorrentButton setHidden: ![torrent publicTorrent]];
268       
269        //allow these fields to be selected
270        [fTrackerField setSelectable: YES];
271        [fHashField setSelectable: YES];
272        [fCommentView setSelectable: ![commentString isEqualToString: @""]];
273        [fCreatorField setSelectable: ![creatorString isEqualToString: @""]];
274        [fTorrentLocationField setSelectable: YES];
275        [fDataLocationField setSelectable: YES];
276       
277        //set pieces view
278        BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"];
279        [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE];
280        [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS];
281        [fPiecesControl setEnabled: YES];
282        [fPiecesView setTorrent: torrent];
283       
284        //set file table
285        [fFileOutline deselectAll: nil];
286        [fFiles release];
287        fFiles = [[torrent fileList] retain];
288       
289        [self updateInfoFiles];
290       
291        int fileCount = [torrent fileCount];
292        if (fileCount != 1)
293            [fFileTableStatusField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d files total",
294                                "Inspector -> Files tab -> bottom text (number of files)"), fileCount]];
295        else
296            [fFileTableStatusField setStringValue: NSLocalizedString(@"1 file total",
297                                "Inspector -> Files tab -> bottom text (number of files)")];
298    }
299   
300    //update stats and settings
301    [self updateInfoSettings];
302   
303    [fPeerTable reloadData];
304    [fFileOutline deselectAll: nil];
305    [fFileOutline reloadData];
306}
307
308- (Torrent *) selectedTorrent
309{
310    return fTorrents && [fTorrents count] > 0 ? [fTorrents objectAtIndex: 0] : nil;
311}
312
313- (void) updateInfoStats
314{
315    NSString * ident = [[fTabView selectedTabViewItem] identifier];
316    if ([ident isEqualToString: TAB_ACTIVITY_IDENT])
317        [self updateInfoActivity];
318    else if ([ident isEqualToString: TAB_PEERS_IDENT])
319        [self updateInfoPeers];
320    else if ([ident isEqualToString: TAB_INFO_IDENT])
321        [self updateInfoGeneral];
322    else if ([ident isEqualToString: TAB_FILES_IDENT])
323        [self updateInfoFiles];
324    else;
325}
326
327- (void) updateInfoGeneral
328{   
329    if ([fTorrents count] != 1)
330        return;
331   
332    Torrent * torrent = [fTorrents objectAtIndex: 0];
333   
334    NSString * tracker = [[torrent trackerAddress] stringByAppendingString: [torrent trackerAddressAnnounce]];
335    [fTrackerField setStringValue: tracker];
336    [fTrackerField setToolTip: tracker];
337   
338    NSString * location = [torrent dataLocation];
339    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
340    [fDataLocationField setToolTip: location];
341}
342
343- (void) updateInfoActivity
344{
345    int numberSelected = [fTorrents count];
346    if (numberSelected == 0)
347        return;
348   
349    uint64_t  downloadedValid = 0, downloadedTotal = 0, uploadedTotal = 0;
350    Torrent * torrent;
351    NSEnumerator * enumerator = [fTorrents objectEnumerator];
352    while ((torrent = [enumerator nextObject]))
353    {
354        downloadedValid += [torrent downloadedValid];
355        downloadedTotal += [torrent downloadedTotal];
356        uploadedTotal += [torrent uploadedTotal];
357    }
358
359    [fDownloadedValidField setStringValue: [NSString stringForFileSize: downloadedValid]];
360    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
361    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
362   
363    if (numberSelected == 1)
364    {
365        torrent = [fTorrents objectAtIndex: 0];
366       
367        [fStateField setStringValue: [torrent stateString]];
368        [fProgressField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
369                    "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
370        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
371        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
372       
373        NSString * errorMessage = [torrent errorMessage];
374        if (![errorMessage isEqualToString: [fErrorMessageView string]])
375        {
376            [fErrorMessageView setString: errorMessage];
377            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
378        }
379       
380        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
381        [fDateActivityField setObjectValue: [torrent dateActivity]];
382       
383        [fPiecesView updateView: NO];
384    }
385}
386
387- (void) updateInfoPeers
388{
389    if ([fTorrents count] != 1)
390        return;
391    Torrent * torrent = [fTorrents objectAtIndex: 0];
392   
393    int seeders = [torrent seeders], leechers = [torrent leechers], downloaded = [torrent completedFromTracker];
394    [fSeedersField setStringValue: seeders < 0 ? @"" : [NSString stringWithInt: seeders]];
395    [fLeechersField setStringValue: leechers < 0 ? @"" : [NSString stringWithInt: leechers]];
396    [fCompletedFromTrackerField setStringValue: downloaded < 0 ? @"" : [NSString stringWithInt: downloaded]];
397   
398    BOOL active = [torrent isActive];
399   
400    if (active)
401    {
402        int total = [torrent totalPeersConnected];
403        NSString * connected = [NSString stringWithFormat:
404                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
405       
406        if (total > 0)
407        {
408            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
409            int count;
410            if ((count = [torrent totalPeersTracker]) > 0)
411                [components addObject: [NSString stringWithFormat:
412                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
413            if ((count = [torrent totalPeersIncoming]) > 0)
414                [components addObject: [NSString stringWithFormat:
415                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
416            if ((count = [torrent totalPeersPex]) > 0)
417                [components addObject: [NSString stringWithFormat:
418                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
419            if ((count = [torrent totalPeersCache]) > 0)
420                [components addObject: [NSString stringWithFormat:
421                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
422           
423            connected = [NSString stringWithFormat: @"%@: %@", connected, [components componentsJoinedByString: @", "]];
424        }
425       
426        [fConnectedPeersField setStringValue: connected];
427    }
428    else
429        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
430   
431    [fDownloadingFromField setStringValue: active ? [NSString stringWithInt: [torrent peersSendingToUs]] : @""];
432    [fUploadingToField setStringValue: active ? [NSString stringWithInt: [torrent peersGettingFromUs]] : @""];
433   
434    [fPeers release];
435    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
436   
437    [fPeerTable reloadData];
438}
439
440- (void) updateInfoFiles
441{
442    if ([fTorrents count] == 1)
443    {
444        [[fTorrents objectAtIndex: 0] updateFileStat];
445        [fFileOutline reloadData];
446    }
447}
448
449- (void) updateInfoSettings
450{
451    if ([fTorrents count] > 0)
452    {
453        Torrent * torrent;
454       
455        //get bandwidth info
456        NSEnumerator * enumerator = [fTorrents objectEnumerator];
457        torrent = [enumerator nextObject]; //first torrent
458       
459        int uploadSpeedMode = [torrent speedMode: YES],
460            uploadSpeedLimit = [torrent speedLimit: YES],
461            downloadSpeedMode = [torrent speedMode: NO],
462            downloadSpeedLimit = [torrent speedLimit: NO];
463       
464        while ((uploadSpeedMode != INVALID || uploadSpeedLimit != INVALID
465                || downloadSpeedMode != INVALID || downloadSpeedLimit != INVALID)
466                && (torrent = [enumerator nextObject]))
467        {
468            if (uploadSpeedMode != INVALID && uploadSpeedMode != [torrent speedMode: YES])
469                uploadSpeedMode = INVALID;
470           
471            if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
472                uploadSpeedLimit = INVALID;
473           
474            if (downloadSpeedMode != INVALID && downloadSpeedMode != [torrent speedMode: NO])
475                downloadSpeedMode = INVALID;
476           
477            if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
478                downloadSpeedLimit = INVALID;
479        }
480       
481        //set upload view
482        int index;
483        if (uploadSpeedMode == TR_SPEEDLIMIT_SINGLE)
484            index = OPTION_POPUP_LIMIT;
485        else if (uploadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
486            index = OPTION_POPUP_NO_LIMIT;
487        else if (uploadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
488            index = OPTION_POPUP_GLOBAL;
489        else
490            index = -1;
491        [fUploadLimitPopUp selectItemAtIndex: index];
492        [fUploadLimitPopUp setEnabled: YES];
493       
494        [fUploadLimitLabel setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
495        [fUploadLimitField setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
496        if (uploadSpeedLimit != INVALID)
497            [fUploadLimitField setIntValue: uploadSpeedLimit];
498        else
499            [fUploadLimitField setStringValue: @""];
500       
501        //set download view
502        if (downloadSpeedMode == TR_SPEEDLIMIT_SINGLE)
503            index = OPTION_POPUP_LIMIT;
504        else if (downloadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
505            index = OPTION_POPUP_NO_LIMIT;
506        else if (downloadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
507            index = OPTION_POPUP_GLOBAL;
508        else
509            index = -1;
510        [fDownloadLimitPopUp selectItemAtIndex: index];
511        [fDownloadLimitPopUp setEnabled: YES];
512       
513        [fDownloadLimitLabel setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
514        [fDownloadLimitField setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
515        if (downloadSpeedLimit != INVALID)
516            [fDownloadLimitField setIntValue: downloadSpeedLimit];
517        else
518            [fDownloadLimitField setStringValue: @""];
519       
520        //get ratio info
521        enumerator = [fTorrents objectEnumerator];
522        torrent = [enumerator nextObject]; //first torrent
523       
524        int checkRatio = [torrent ratioSetting];
525        float ratioLimit = [torrent ratioLimit];
526       
527        while ((checkRatio != INVALID || checkRatio != INVALID)
528                && (torrent = [enumerator nextObject]))
529        {
530            if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
531                checkRatio = INVALID;
532           
533            if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
534                ratioLimit = INVALID;
535        }
536       
537        //set ratio view
538        if (checkRatio == NSOnState)
539            index = OPTION_POPUP_LIMIT;
540        else if (checkRatio == NSOffState)
541            index = OPTION_POPUP_NO_LIMIT;
542        else if (checkRatio == NSMixedState)
543            index = OPTION_POPUP_GLOBAL;
544        else
545            index = -1;
546        [fRatioPopUp selectItemAtIndex: index];
547        [fRatioPopUp setEnabled: YES];
548       
549        [fRatioLimitField setHidden: checkRatio != NSOnState];
550        if (ratioLimit != INVALID)
551            [fRatioLimitField setFloatValue: ratioLimit];
552        else
553            [fRatioLimitField setStringValue: @""];
554               
555                //set pex check
556                enumerator = [fTorrents objectEnumerator];
557        torrent = [enumerator nextObject]; //first torrent
558               
559                BOOL pexEnabled = ![torrent privateTorrent];
560                int pexState = [torrent pex] ? NSOnState : NSOffState;
561               
562                while ((pexEnabled || pexState != NSMixedState)
563                && (torrent = [enumerator nextObject]))
564        {
565            if (pexEnabled)
566                pexEnabled = ![torrent privateTorrent];
567           
568            if (pexState != NSMixedState && pexState != ([torrent pex] ? NSOnState : NSOffState))
569                pexState = NSMixedState;
570        }
571               
572                [fPexCheck setEnabled: pexEnabled];
573                [fPexCheck setState: pexState];
574    }
575    else
576    {
577        [fUploadLimitPopUp setEnabled: NO];
578        [fUploadLimitPopUp selectItemAtIndex: -1];
579        [fUploadLimitField setHidden: YES];
580        [fUploadLimitLabel setHidden: YES];
581        [fUploadLimitField setStringValue: @""];
582       
583        [fDownloadLimitPopUp setEnabled: NO];
584        [fDownloadLimitPopUp selectItemAtIndex: -1];
585        [fDownloadLimitField setHidden: YES];
586        [fDownloadLimitLabel setHidden: YES];
587        [fDownloadLimitField setStringValue: @""];
588       
589        [fRatioPopUp setEnabled: NO];
590        [fRatioPopUp selectItemAtIndex: -1];
591        [fRatioLimitField setHidden: YES];
592        [fRatioLimitField setStringValue: @""];
593               
594                [fPexCheck setEnabled: NO];
595        [fPexCheck setState: NSOffState];
596    }
597   
598    [self updateInfoStats];
599}
600
601- (void) updateRatioForTorrent: (Torrent *) torrent
602{
603    if ([fTorrents containsObject: torrent])
604        [self updateInfoSettings];
605}
606
607- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
608{
609    SEL action = [menuItem action];
610   
611    if (action == @selector(revealFile:))
612        return [fFileOutline numberOfSelectedRows] > 0 &&
613            [[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_FILES_IDENT];
614   
615    if (action == @selector(setCheck:))
616    {
617        if ([fFileOutline numberOfSelectedRows] <= 0)
618            return NO;
619       
620        Torrent * torrent = [fTorrents objectAtIndex: 0];
621        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
622        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
623        int i, state = (menuItem == fFileCheckItem) ? NSOnState : NSOffState;
624        for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
625            [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
626       
627        return [torrent checkForFiles: itemIndexes] != state && [torrent canChangeDownloadCheckForFiles: itemIndexes];
628    }
629   
630    if (action == @selector(setOnlySelectedCheck:))
631    {
632        if ([fFileOutline numberOfSelectedRows] <= 0)
633            return NO;
634       
635        Torrent * torrent = [fTorrents objectAtIndex: 0];
636        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
637        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
638        int i;
639        for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
640            [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
641           
642        return [torrent canChangeDownloadCheckForFiles: itemIndexes];
643    }
644   
645    if (action == @selector(setPriority:))
646    {
647        if ([fFileOutline numberOfSelectedRows] <= 0)
648        {
649            [menuItem setState: NSOffState];
650            return NO;
651        }
652       
653        //determine which priorities are checked
654        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
655        BOOL current = NO, other = NO;
656        int i, priority;
657        Torrent * torrent = [fTorrents objectAtIndex: 0];
658       
659        if (menuItem == fFilePriorityHigh)
660            priority = TR_PRI_HIGH;
661        else if (menuItem == fFilePriorityLow)
662            priority = TR_PRI_LOW;
663        else
664            priority = TR_PRI_NORMAL;
665       
666        NSIndexSet * fileIndexSet;
667        for (i = [indexSet firstIndex]; i != NSNotFound && (!current || !other); i = [indexSet indexGreaterThanIndex: i])
668        {
669            fileIndexSet = [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"];
670            if (![torrent canChangeDownloadCheckForFiles: fileIndexSet])
671                continue;
672            else if ([torrent hasFilePriority: priority forIndexes: fileIndexSet])
673                current = YES;
674            else
675                other = YES;
676        }
677       
678        [menuItem setState: current ? NSOnState : NSOffState];
679        return current || other;
680    }
681   
682    return YES;
683}
684
685- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
686{
687    NSRect windowRect = [window frame];
688    windowRect.size.width = [window minSize].width;
689    return windowRect;
690}
691
692- (void) tabView: (NSTabView *) tabView didSelectTabViewItem: (NSTabViewItem *) tabViewItem
693{
694    NSString * identifier = [tabViewItem identifier];
695    [self setWindowForTab: identifier animate: YES];
696    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
697}
698
699- (void) setWindowForTab: (NSString *) identifier animate: (BOOL) animate
700{
701    [self updateInfoStats];
702   
703    BOOL canResizeVertical = NO;
704    float height;
705    if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
706    {
707        height = TAB_ACTIVITY_HEIGHT;
708        [fPiecesView updateView: YES];
709    }
710    else if ([identifier isEqualToString: TAB_PEERS_IDENT])
711    {
712        height = TAB_PEERS_HEIGHT;
713        canResizeVertical = YES;
714    }
715    else if ([identifier isEqualToString: TAB_FILES_IDENT])
716    {
717        height = TAB_FILES_HEIGHT;
718        canResizeVertical = YES;
719    }
720    else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
721        height = TAB_OPTIONS_HEIGHT;
722    else
723        height = TAB_INFO_HEIGHT;
724   
725    NSWindow * window = [self window];
726    NSRect frame = [window frame];
727    NSView * view = [[fTabView selectedTabViewItem] view];
728   
729    float difference = height - [view frame].size.height;
730    frame.origin.y -= difference;
731    frame.size.height += difference;
732   
733    if (!fCanResizeVertical || !canResizeVertical)
734    {
735        if (animate)
736        {
737            [view setHidden: YES];
738            [window setFrame: frame display: YES animate: YES];
739            [view setHidden: NO];
740        }
741        else
742            [window setFrame: frame display: YES];
743    }
744   
745    [window setMinSize: NSMakeSize([window minSize].width, frame.size.height)];
746    [window setMaxSize: NSMakeSize([window maxSize].width, canResizeVertical ? FLT_MAX : frame.size.height)];
747   
748    fCanResizeVertical = canResizeVertical;
749}
750
751- (void) setNextTab
752{
753    if ([fTabView indexOfTabViewItem: [fTabView selectedTabViewItem]] == [fTabView numberOfTabViewItems] - 1)
754        [fTabView selectFirstTabViewItem: nil];
755    else
756        [fTabView selectNextTabViewItem: nil];
757}
758
759- (void) setPreviousTab
760{
761    if ([fTabView indexOfTabViewItem: [fTabView selectedTabViewItem]] == 0)
762        [fTabView selectLastTabViewItem: nil];
763    else
764        [fTabView selectPreviousTabViewItem: nil];
765}
766
767- (int) numberOfRowsInTableView: (NSTableView *) tableView
768{
769    if (tableView == fPeerTable)
770        return fPeers ? [fPeers count] : 0;
771    return 0;
772}
773
774- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
775{
776    if (tableView == fPeerTable)
777    {
778        NSString * ident = [column identifier];
779        NSDictionary * peer = [fPeers objectAtIndex: row];
780       
781        if ([ident isEqualToString: @"Connected"])
782            return [[peer objectForKey: @"Connected"] boolValue] ? fDotGreen : fDotRed;
783        else if ([ident isEqualToString: @"Client"])
784            return [peer objectForKey: @"Client"];
785        else if  ([ident isEqualToString: @"Progress"])
786            return [peer objectForKey: @"Progress"]; //returning nil is fine
787        else if ([ident isEqualToString: @"UL To"])
788        {
789            NSNumber * rate;
790            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
791        }
792        else if ([ident isEqualToString: @"DL From"])
793        {
794            NSNumber * rate;
795            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
796        }
797        else
798            return [peer objectForKey: @"IP"];
799    }
800    return nil;
801}
802
803- (void) tableView: (NSTableView *) tableView willDisplayCell: (id) cell
804            forTableColumn: (NSTableColumn *) tableColumn row: (int) row
805{
806    if (tableView == fPeerTable)
807    {
808        if ([[tableColumn identifier] isEqualToString: @"Progress"])
809            [cell setHidden: ![[[fPeers objectAtIndex: row] objectForKey: @"Connected"] boolValue]];
810    }
811}
812
813- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
814{
815    if (tableView == fPeerTable)
816    {
817        if (fPeers)
818        {
819            NSArray * oldPeers = fPeers;
820            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
821            [oldPeers release];
822            [tableView reloadData];
823        }
824    }
825}
826
827- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow:(int) row
828{
829    return tableView != fPeerTable;
830}
831
832- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
833                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
834{
835    if (tableView == fPeerTable)
836    {
837        NSDictionary * peer = [fPeers objectAtIndex: row];
838       
839        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 3];
840       
841        if ([[peer objectForKey: @"Connected"] boolValue])
842            [components addObject: [NSString stringWithFormat:
843                                    NSLocalizedString(@"Progress: %.1f%%", "Inspector -> Peers tab -> table row tooltip"),
844                                    [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
845       
846        int port;
847        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
848            [components addObject: [NSString stringWithFormat:
849                                    NSLocalizedString(@"Port: %d", "Inspector -> Peers tab -> table row tooltip"), port]];
850        else
851            [components addObject: NSLocalizedString(@"Port: N/A", "Inspector -> Peers tab -> table row tooltip")];
852       
853        int from = [[peer objectForKey: @"From"] intValue];
854        if (from == TR_PEER_FROM_INCOMING)
855            [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
856        else if (from == TR_PEER_FROM_CACHE)
857            [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
858        else if (from == TR_PEER_FROM_PEX)
859            [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
860        else
861            [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
862       
863        return [components componentsJoinedByString: @"\n"];
864    }
865    return nil;
866}
867
868- (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
869{
870    if (!item)
871        return [fFiles count];
872    return [[item objectForKey: @"IsFolder"] boolValue] ? [[item objectForKey: @"Children"] count] : 0;
873}
874
875- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
876{
877    return [[item objectForKey: @"IsFolder"] boolValue];
878}
879
880- (id) outlineView: (NSOutlineView *) outlineView child: (int) index ofItem: (id) item
881{
882    return [(item ? [item objectForKey: @"Children"] : fFiles) objectAtIndex: index];
883}
884
885- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
886{
887    if ([[tableColumn identifier] isEqualToString: @"Check"])
888        return [NSNumber numberWithInt: [[fTorrents objectAtIndex: 0] checkForFiles: [item objectForKey: @"Indexes"]]];
889    else
890        return item;
891}
892
893- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
894            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
895{
896    NSString * identifier = [tableColumn identifier];
897    if ([identifier isEqualToString: @"Check"])
898        [cell setEnabled: [[fTorrents objectAtIndex: 0] canChangeDownloadCheckForFiles: [item objectForKey: @"Indexes"]]];
899    else if ([identifier isEqualToString: @"Priority"])
900        [cell setRepresentedObject: item];
901    else;
902}
903
904- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
905        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
906{
907    NSString * identifier = [tableColumn identifier];
908    if ([identifier isEqualToString: @"Check"])
909    {
910        Torrent * torrent = [fTorrents objectAtIndex: 0];
911        NSIndexSet * indexSet;
912        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
913            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
914        else
915            indexSet = [item objectForKey: @"Indexes"];
916       
917        [torrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
918        [fFileOutline reloadData];
919       
920        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
921    }
922}
923
924- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
925        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
926{
927    NSString * ident = [tableColumn identifier];
928    if ([ident isEqualToString: @"Name"])
929        return [[[fTorrents objectAtIndex: 0] downloadFolder] stringByAppendingPathComponent: [item objectForKey: @"Path"]];
930    else if ([ident isEqualToString: @"Check"])
931    {
932        int check = [cell state];
933        if (check == NSOffState)
934            return NSLocalizedString(@"Don't Download", "Inspector -> files tab -> tooltip");
935        else if (check == NSMixedState)
936            return NSLocalizedString(@"Download Some", "Inspector -> files tab -> tooltip");
937        else
938            return NSLocalizedString(@"Download", "Inspector -> files tab -> tooltip");
939    }
940    else if ([ident isEqualToString: @"Priority"])
941    {
942        NSSet * priorities = [[fTorrents objectAtIndex: 0] filePrioritiesForIndexes: [item objectForKey: @"Indexes"]];
943       
944        int count = [priorities count];
945        if (count == 0)
946            return NSLocalizedString(@"Priority Not Available", "Inspector -> files tab -> tooltip");
947        else if (count > 1)
948            return NSLocalizedString(@"Multiple Priorities", "Inspector -> files tab -> tooltip");
949        else
950        {
951            int priority = [[priorities anyObject] intValue];
952            if (priority == TR_PRI_LOW)
953                return NSLocalizedString(@"Low Priority", "Inspector -> files tab -> tooltip");
954            else if (priority == TR_PRI_HIGH)
955                return NSLocalizedString(@"High Priority", "Inspector -> files tab -> tooltip");
956            else
957                return NSLocalizedString(@"Normal Priority", "Inspector -> files tab -> tooltip");
958        }
959    }
960    else
961        return nil;
962}
963
964- (float) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
965{
966    if ([[item objectForKey: @"IsFolder"] boolValue])
967        return FILE_ROW_SMALL_HEIGHT;
968    else
969        return [outlineView rowHeight];
970}
971
972- (void) setFileOutlineHoverRowForEvent: (NSEvent *) event
973{
974    [fFileOutline setHoverRowForEvent: [[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_FILES_IDENT]
975                                        ? event : nil];
976}
977
978- (NSArray *) peerSortDescriptors
979{
980    NSMutableArray * descriptors = [NSMutableArray array];
981   
982    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
983    BOOL useSecond = YES, asc = YES;
984    if ([oldDescriptors count] > 0)
985    {
986        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
987        [descriptors addObject: descriptor];
988       
989        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
990            asc = [descriptor ascending];
991    }
992   
993    //sort by IP after primary sort
994    if (useSecond)
995    {
996        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
997                                                                        selector: @selector(compareIP:)];
998        [descriptors addObject: secondDescriptor];
999        [secondDescriptor release];
1000    }
1001   
1002    return descriptors;
1003}
1004
1005- (void) setPiecesView: (id) sender
1006{
1007    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
1008}
1009
1010- (void) setPiecesViewForAvailable: (BOOL) available
1011{
1012    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
1013    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
1014   
1015    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
1016    [fPiecesView updateView: YES];
1017}
1018
1019- (void) revealTorrentFile: (id) sender
1020{
1021    if ([fTorrents count] > 0)
1022        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
1023}
1024
1025- (void) revealDataFile: (id) sender
1026{
1027    if ([fTorrents count] > 0)
1028        [[fTorrents objectAtIndex: 0] revealData];
1029}
1030
1031- (void) revealFile: (id) sender
1032{
1033    if (!fFiles)
1034        return;
1035   
1036    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
1037    NSIndexSet * indexes = [fFileOutline selectedRowIndexes];
1038    int i;
1039    for (i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
1040        [[NSWorkspace sharedWorkspace] selectFile: [folder stringByAppendingPathComponent:
1041                [[fFileOutline itemAtRow: i] objectForKey: @"Path"]] inFileViewerRootedAtPath: nil];
1042}
1043
1044- (void) setCheck: (id) sender
1045{
1046    int state = sender == fFileCheckItem ? NSOnState : NSOffState;
1047   
1048    Torrent * torrent = [fTorrents objectAtIndex: 0];
1049    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1050    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1051    int i;
1052    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1053        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1054   
1055    [torrent setFileCheckState: state forIndexes: itemIndexes];
1056    [fFileOutline reloadData];
1057}
1058
1059- (void) setOnlySelectedCheck: (id) sender
1060{
1061    Torrent * torrent = [fTorrents objectAtIndex: 0];
1062    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1063    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1064    int i;
1065    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1066        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1067   
1068    [torrent setFileCheckState: NSOnState forIndexes: itemIndexes];
1069   
1070    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
1071    [remainingItemIndexes removeIndexes: itemIndexes];
1072    [torrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
1073   
1074    [fFileOutline reloadData];
1075}
1076
1077- (void) setPriority: (id) sender
1078{
1079    int priority;
1080    if (sender == fFilePriorityHigh)
1081        priority = TR_PRI_HIGH;
1082    else if (sender == fFilePriorityLow)
1083        priority = TR_PRI_LOW;
1084    else
1085        priority = TR_PRI_NORMAL;
1086   
1087    Torrent * torrent = [fTorrents objectAtIndex: 0];
1088    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1089    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1090    int i;
1091    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1092        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1093   
1094    [torrent setFilePriority: priority forIndexes: itemIndexes];
1095    [fFileOutline reloadData];
1096}
1097
1098- (void) setSpeedMode: (id) sender
1099{
1100    BOOL upload = sender == fUploadLimitPopUp;
1101    int index = [sender indexOfSelectedItem], mode;
1102    if (index == OPTION_POPUP_LIMIT)
1103        mode = TR_SPEEDLIMIT_SINGLE;
1104    else if (index == OPTION_POPUP_NO_LIMIT)
1105        mode = TR_SPEEDLIMIT_UNLIMITED;
1106    else
1107        mode = TR_SPEEDLIMIT_GLOBAL;
1108   
1109    Torrent * torrent;
1110    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1111    while ((torrent = [enumerator nextObject]))
1112        [torrent setSpeedMode: mode upload: upload];
1113   
1114    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
1115   
1116    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
1117    [field setHidden: !single];
1118    if (single)
1119    {
1120        [field selectText: self];
1121        [[self window] makeKeyAndOrderFront:self];
1122    }
1123   
1124    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
1125    [label setHidden: !single];
1126}
1127
1128- (void) setSpeedLimit: (id) sender
1129{
1130    BOOL upload = sender == fUploadLimitField;
1131   
1132    Torrent * torrent;
1133    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1134
1135    int limit = [sender intValue];
1136    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%i", limit]] || limit < 0)
1137    {
1138        NSBeep();
1139       
1140        torrent = [enumerator nextObject]; //use first torrent
1141        limit = [torrent speedLimit: upload];
1142        while ((torrent = [enumerator nextObject]))
1143            if (limit != [torrent speedLimit: upload])
1144            {
1145                [sender setStringValue: @""];
1146                return;
1147            }
1148       
1149        [sender setIntValue: limit];
1150    }
1151    else
1152    {
1153        while ((torrent = [enumerator nextObject]))
1154            [torrent setSpeedLimit: limit upload: upload];
1155    }
1156}
1157
1158- (void) setRatioSetting: (id) sender
1159{
1160    int index = [sender indexOfSelectedItem], setting;
1161    if (index == OPTION_POPUP_LIMIT)
1162        setting = NSOnState;
1163    else if (index == OPTION_POPUP_NO_LIMIT)
1164        setting = NSOffState;
1165    else
1166        setting = NSMixedState;
1167   
1168    Torrent * torrent;
1169    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1170    while ((torrent = [enumerator nextObject]))
1171        [torrent setRatioSetting: setting];
1172   
1173    BOOL single = setting == NSOnState;
1174    [fRatioLimitField setHidden: !single];
1175    if (single)
1176    {
1177        [fRatioLimitField selectText: self];
1178        [[self window] makeKeyAndOrderFront:self];
1179    }
1180   
1181    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1182}
1183
1184- (void) setRatioLimit: (id) sender
1185{
1186    Torrent * torrent;
1187    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1188
1189    float ratioLimit = [sender floatValue];
1190    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]] || ratioLimit < 0)
1191    {
1192        NSBeep();
1193        float ratioLimit = [[enumerator nextObject] ratioLimit]; //use first torrent
1194        while ((torrent = [enumerator nextObject]))
1195            if (ratioLimit != [torrent ratioLimit])
1196            {
1197                [sender setStringValue: @""];
1198                return;
1199            }
1200       
1201        [sender setFloatValue: ratioLimit];
1202    }
1203    else
1204    {
1205        while ((torrent = [enumerator nextObject]))
1206            [torrent setRatioLimit: ratioLimit];
1207    }
1208   
1209    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1210}
1211
1212- (void) setPex: (id) sender
1213{
1214        int state = [sender state];
1215        if (state == NSMixedState)
1216        {
1217                state = NSOnState;
1218                [sender setState: state];
1219        }
1220       
1221        Torrent * torrent;
1222    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1223       
1224        while ((torrent = [enumerator nextObject]))
1225                [torrent setPex: state == NSOnState];
1226}
1227
1228@end
Note: See TracBrowser for help on using the repository browser.