source: trunk/macosx/InfoWindowController.m @ 2180

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

remove a few duplicate calls

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