source: trunk/macosx/InfoWindowController.m @ 2811

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

again by popular demand, allow peers and files tabs to be resized vertically

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