source: trunk/macosx/InfoWindowController.m @ 2194

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

get the "only check selected" item working

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