source: trunk/macosx/InfoWindowController.m @ 2178

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

add a menu item in the context menu to only check selected (and uncheck others)

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