source: trunk/macosx/InfoWindowController.m @ 2179

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

cleaned up the pieces view segment control code a bit, and clicking the view will toggle it

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