source: trunk/macosx/InfoWindowController.m @ 9301

Last change on this file since 9301 was 9301, checked in by livings124, 13 years ago

don't zoom the QL window if the main window's closed

  • Property svn:keywords set to Date Rev Author Id
File size: 63.9 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 9301 2009-10-12 00:19:04Z livings124 $
3 *
4 * Copyright (c) 2006-2009 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 "FileListNode.h"
27#import "FileOutlineController.h"
28#import "FileOutlineView.h"
29#import "InfoTabButtonCell.h"
30#import "NSApplicationAdditions.h"
31#import "NSStringAdditions.h"
32#import "PeerProgressIndicatorCell.h"
33#import "PiecesView.h"
34#import "Torrent.h"
35#import "TrackerCell.h"
36#import "TrackerNode.h"
37#import "TrackerTableView.h"
38#include "utils.h" //tr_getRatio()
39
40#define TAB_INFO_IDENT @"Info"
41#define TAB_ACTIVITY_IDENT @"Activity"
42#define TAB_TRACKER_IDENT @"Tracker"
43#define TAB_PEERS_IDENT @"Peers"
44#define TAB_FILES_IDENT @"Files"
45#define TAB_OPTIONS_IDENT @"Options"
46
47#define TAB_MIN_HEIGHT 250
48
49#define TRACKER_GROUP_SEPARATOR_HEIGHT 14.0
50
51#define PIECES_CONTROL_PROGRESS 0
52#define PIECES_CONTROL_AVAILABLE 1
53
54#define OPTION_POPUP_GLOBAL 0
55#define OPTION_POPUP_NO_LIMIT 1
56#define OPTION_POPUP_LIMIT 2
57
58#define OPTION_POPUP_PRIORITY_HIGH 0
59#define OPTION_POPUP_PRIORITY_NORMAL 1
60#define OPTION_POPUP_PRIORITY_LOW 2
61
62#define INVALID -99
63
64#define TRACKER_ADD_TAG 0
65#define TRACKER_REMOVE_TAG 1
66
67typedef enum
68{
69    TAB_INFO_TAG = 0,
70    TAB_ACTIVITY_TAG = 1,
71    TAB_TRACKER_TAG = 2,
72    TAB_PEERS_TAG = 3,
73    TAB_FILES_TAG = 4,
74    TAB_OPTIONS_TAG = 5
75} tabTag;
76
77@interface InfoWindowController (Private)
78
79- (void) updateInfoGeneral;
80- (void) updateInfoActivity;
81- (void) updateInfoTracker;
82- (void) updateInfoPeers;
83- (void) updateInfoFiles;
84
85- (NSView *) tabViewForTag: (NSInteger) tag;
86- (void) setWebSeedTableHidden: (BOOL) hide animate: (BOOL) animate;
87- (NSArray *) peerSortDescriptors;
88
89- (BOOL) canQuickLookFile: (FileListNode *) item;
90
91- (void) addTrackers;
92- (void) removeTrackers;
93
94@end
95
96@implementation InfoWindowController
97
98- (id) init
99{
100    if ((self = [super initWithWindowNibName: @"InfoWindow"]))
101    {
102        fTrackerCell = [[TrackerCell alloc] init];
103    }
104   
105    return self;
106}
107
108- (void) awakeFromNib
109{
110    //window location and size
111    NSPanel * window = (NSPanel *)[self window];
112   
113    CGFloat windowHeight = [window frame].size.height;
114   
115    [window setFrameAutosaveName: @"InspectorWindow"];
116    [window setFrameUsingName: @"InspectorWindow"];
117   
118    NSRect windowRect = [window frame];
119    windowRect.origin.y -= windowHeight - windowRect.size.height;
120    windowRect.size.height = windowHeight;
121    [window setFrame: windowRect display: NO];
122   
123    [window setBecomesKeyOnlyIfNeeded: YES];
124   
125    //set tab images and tooltips
126    [[fTabMatrix cellWithTag: TAB_INFO_TAG] setIcon: [NSImage imageNamed: @"InfoGeneral.png"]];
127    [[fTabMatrix cellWithTag: TAB_ACTIVITY_TAG] setIcon: [NSImage imageNamed: @"InfoActivity.png"]];
128    [[fTabMatrix cellWithTag: TAB_TRACKER_TAG] setIcon: [NSImage imageNamed: @"InfoTracker.png"]];
129    [[fTabMatrix cellWithTag: TAB_PEERS_TAG] setIcon: [NSImage imageNamed: @"InfoPeers.png"]];
130    [[fTabMatrix cellWithTag: TAB_FILES_TAG] setIcon: [NSImage imageNamed: @"InfoFiles.png"]];
131    [[fTabMatrix cellWithTag: TAB_OPTIONS_TAG] setIcon: [NSImage imageNamed: @"InfoOptions.png"]];
132   
133    //set selected tab
134    fCurrentTabTag = INVALID;
135    NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"];
136    NSInteger tag;
137    if ([identifier isEqualToString: TAB_INFO_IDENT])
138        tag = TAB_INFO_TAG;
139    else if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
140        tag = TAB_ACTIVITY_TAG;
141    else if ([identifier isEqualToString: TAB_TRACKER_IDENT])
142        tag = TAB_TRACKER_TAG;
143    else if ([identifier isEqualToString: TAB_PEERS_IDENT])
144        tag = TAB_PEERS_TAG;
145    else if ([identifier isEqualToString: TAB_FILES_IDENT])
146        tag = TAB_FILES_TAG;
147    else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
148        tag = TAB_OPTIONS_TAG;
149    else //safety
150    {
151        [[NSUserDefaults standardUserDefaults] setObject: TAB_INFO_IDENT forKey: @"InspectorSelected"];
152        tag = TAB_INFO_TAG;
153    }
154    [fTabMatrix selectCellWithTag: tag];
155    [self setTab: nil];
156   
157    //reset images for reveal button, since the images are also used in the main table
158    NSImage * revealOn = [[NSImage imageNamed: @"RevealOn.png"] copy],
159            * revealOff = [[NSImage imageNamed: @"RevealOff.png"] copy];
160    if (![NSApp isOnSnowLeopardOrBetter])
161    {
162        [revealOn setFlipped: NO];
163        [revealOff setFlipped: NO];
164    }
165   
166    [fRevealDataButton setImage: revealOff];
167    [fRevealDataButton setAlternateImage: revealOn];
168   
169    [revealOn release];
170    [revealOff release];
171   
172    //initially sort peer table by IP
173    if ([[fPeerTable sortDescriptors] count] == 0)
174        [fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
175                                            sortDescriptorPrototype]]];
176   
177    //initially sort webseed table by address
178    if ([[fWebSeedTable sortDescriptors] count] == 0)
179        [fWebSeedTable setSortDescriptors: [NSArray arrayWithObject: [[fWebSeedTable tableColumnWithIdentifier: @"Address"]
180                                            sortDescriptorPrototype]]];
181   
182    //set table header tool tips
183    [[fPeerTable tableColumnWithIdentifier: @"Encryption"] setHeaderToolTip: NSLocalizedString(@"Encrypted Connection",
184                                                                        "inspector -> peer table -> header tool tip")];
185    [[fPeerTable tableColumnWithIdentifier: @"Progress"] setHeaderToolTip: NSLocalizedString(@"Available",
186                                                                        "inspector -> peer table -> header tool tip")];
187    [[fPeerTable tableColumnWithIdentifier: @"UL To"] setHeaderToolTip: NSLocalizedString(@"Uploading To Peer",
188                                                                        "inspector -> peer table -> header tool tip")];
189    [[fPeerTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Peer",
190                                                                        "inspector -> peer table -> header tool tip")];
191   
192    [[fWebSeedTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Web Seed",
193                                                                        "inspector -> web seed table -> header tool tip")];
194   
195    //prepare for animating peer table and web seed table
196    NSRect webSeedTableFrame = [[fWebSeedTable enclosingScrollView] frame];
197    fWebSeedTableHeight = webSeedTableFrame.size.height;
198    fSpaceBetweenWebSeedAndPeer = webSeedTableFrame.origin.y - NSMaxY([[fPeerTable enclosingScrollView] frame]);
199   
200    [self setWebSeedTableHidden: YES animate: NO];
201   
202    //set blank inspector
203    [self setInfoForTorrents: [NSArray array]];
204   
205    //allow for update notifications
206    NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
207    [nc addObserver: self selector: @selector(updateInfoStats) name: @"UpdateStats" object: nil];
208    [nc addObserver: self selector: @selector(updateOptions) name: @"UpdateOptions" object: nil];
209}
210
211- (void) dealloc
212{
213    //save resizeable view height
214    NSString * resizeSaveKey = nil;
215    switch (fCurrentTabTag)
216    {
217        case TAB_TRACKER_TAG:
218            resizeSaveKey = @"InspectorContentHeightTracker";
219            break;
220        case TAB_PEERS_TAG:
221            resizeSaveKey = @"InspectorContentHeightPeers";
222            break;
223        case TAB_FILES_TAG:
224            resizeSaveKey = @"InspectorContentHeightFiles";
225            break;
226    }
227    if (resizeSaveKey)
228        [[NSUserDefaults standardUserDefaults] setFloat: [[self tabViewForTag: fCurrentTabTag] frame].size.height forKey: resizeSaveKey];
229   
230    [[NSNotificationCenter defaultCenter] removeObserver: self];
231   
232    [fTorrents release];
233    [fPeers release];
234    [fWebSeeds release];
235    [fTrackers release];
236   
237    [fWebSeedTableAnimation release];
238   
239    [fTrackerCell release];
240   
241    [fPreviewPanel release];
242   
243    [super dealloc];
244}
245
246- (void) setInfoForTorrents: (NSArray *) torrents
247{
248    if (fTorrents && [fTorrents isEqualToArray: torrents])
249        return;
250   
251    [fTorrents release];
252    fTorrents = [torrents retain];
253
254    NSUInteger numberSelected = [fTorrents count];
255    if (numberSelected != 1)
256    {
257        if (numberSelected > 0)
258        {
259            [fImageView setImage: [NSImage imageNamed: NSImageNameMultipleDocuments]];
260           
261            [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d Torrents Selected",
262                                            "Inspector -> selected torrents"), numberSelected]];
263       
264            uint64_t size = 0;
265            NSInteger fileCount = 0;
266            for (Torrent * torrent in torrents)
267            {
268                size += [torrent size];
269                fileCount += [torrent fileCount];
270            }
271           
272            [fBasicInfoField setStringValue: [NSString stringWithFormat: @"%@, %@",
273                [NSString stringWithFormat: NSLocalizedString(@"%d files", "Inspector -> selected torrents"), fileCount],
274                [NSString stringWithFormat: NSLocalizedString(@"%@ total", "Inspector -> selected torrents"),
275                [NSString stringForFileSize: size]]]];
276            [fBasicInfoField setToolTip: [NSString stringWithFormat: NSLocalizedString(@"%llu bytes", "Inspector -> selected torrents"),
277                                            size]];
278        }
279        else
280        {
281            [fImageView setImage: [NSImage imageNamed: @"NSApplicationIcon"]];
282           
283            [fNameField setStringValue: NSLocalizedString(@"No Torrents Selected", "Inspector -> selected torrents")];
284            [fBasicInfoField setStringValue: @""];
285            [fBasicInfoField setToolTip: @""];
286   
287            [fHaveField setStringValue: @""];
288            [fDownloadedTotalField setStringValue: @""];
289            [fUploadedTotalField setStringValue: @""];
290            [fFailedHashField setStringValue: @""];
291            [fDateActivityField setStringValue: @""];
292            [fRatioField setStringValue: @""];
293           
294            //options fields
295            [fUploadLimitCheck setEnabled: NO];
296            [fUploadLimitCheck setState: NSOffState];
297            [fUploadLimitField setEnabled: NO];
298            [fUploadLimitLabel setEnabled: NO];
299            [fUploadLimitField setStringValue: @""];
300           
301            [fDownloadLimitCheck setEnabled: NO];
302            [fDownloadLimitCheck setState: NSOffState];
303            [fDownloadLimitField setEnabled: NO];
304            [fDownloadLimitLabel setEnabled: NO];
305            [fDownloadLimitField setStringValue: @""];
306           
307            [fGlobalLimitCheck setEnabled: NO];
308            [fGlobalLimitCheck setState: NSOffState];
309           
310            [fPriorityPopUp setEnabled: NO];
311            [fPriorityPopUp selectItemAtIndex: -1];
312           
313            [fRatioPopUp setEnabled: NO];
314            [fRatioPopUp selectItemAtIndex: -1];
315            [fRatioLimitField setHidden: YES];
316            [fRatioLimitField setStringValue: @""];
317           
318            [fPeersConnectField setEnabled: NO];
319            [fPeersConnectField setStringValue: @""];
320            [fPeersConnectLabel setEnabled: NO];
321        }
322       
323        [fFileController setTorrent: nil];
324       
325        [fNameField setToolTip: nil];
326
327        [fPiecesField setStringValue: @""];
328        [fHashField setStringValue: @""];
329        [fHashField setToolTip: nil];
330        [fSecureField setStringValue: @""];
331        [fCommentView setString: @""];
332       
333        [fCreatorField setStringValue: @""];
334        [fDateCreatedField setStringValue: @""];
335        [fCommentView setSelectable: NO];
336       
337        [fDataLocationField setStringValue: @""];
338        [fDataLocationField setToolTip: nil];
339       
340        [fRevealDataButton setHidden: YES];
341       
342        //don't allow empty fields to be selected
343        [fHashField setSelectable: NO];
344        [fCreatorField setSelectable: NO];
345        [fDataLocationField setSelectable: NO];
346       
347        [fStateField setStringValue: @""];
348        [fProgressField setStringValue: @""];
349       
350        [fSwarmSpeedField setStringValue: @""];
351        [fErrorMessageView setString: @""];
352        [fErrorMessageView setSelectable: NO];
353       
354        [fConnectedPeersField setStringValue: @""];
355        [fDownloadingFromField setStringValue: @""];
356        [fUploadingToField setStringValue: @""];
357        [fKnownField setStringValue: @""];
358       
359        [fDateAddedField setStringValue: @""];
360        [fDateCompletedField setStringValue: @""];
361       
362        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE];
363        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS];
364        [fPiecesControl setEnabled: NO];
365        [fPiecesView setTorrent: nil];
366       
367        [fPeers release];
368        fPeers = nil;
369        [fPeerTable reloadData];
370       
371        [fWebSeeds release];
372        fWebSeeds = nil;
373        [fWebSeedTable reloadData];
374        [self setWebSeedTableHidden: YES animate: YES];
375       
376        [fTrackerTable setTorrent: nil];
377       
378        [fTrackers release];
379        fTrackers = nil;
380       
381        [fTrackerTable setTrackers: fTrackers];
382        [fTrackerTable reloadData];
383       
384        [fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_ADD_TAG];
385        [fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_REMOVE_TAG];
386       
387        [fFileFilterField setEnabled: NO];
388    }
389    else
390    {
391        Torrent * torrent = [fTorrents objectAtIndex: 0];
392       
393        [fFileController setTorrent: torrent];
394       
395        if ([NSApp isOnSnowLeopardOrBetter])
396            [fImageView setImage: [torrent icon]];
397        else
398        {
399            NSImage * icon = [[torrent icon] copy];
400            [icon setFlipped: NO];
401            [fImageView setImage: icon];
402            [icon release];
403        }
404       
405        NSString * name = [torrent name];
406        [fNameField setStringValue: name];
407        [fNameField setToolTip: name];
408       
409        NSString * basicString = [NSString stringForFileSize: [torrent size]];
410        if ([torrent isFolder])
411        {
412            NSString * fileString;
413            NSInteger fileCount = [torrent fileCount];
414            if (fileCount == 1)
415                fileString = NSLocalizedString(@"1 file", "Inspector -> selected torrents");
416            else
417                fileString= [NSString stringWithFormat: NSLocalizedString(@"%d files", "Inspector -> selected torrents"), fileCount];
418            basicString = [NSString stringWithFormat: @"%@, %@", fileString, basicString];
419        }
420        [fBasicInfoField setStringValue: basicString];
421        [fBasicInfoField setToolTip: [NSString stringWithFormat: NSLocalizedString(@"%llu bytes", "Inspector -> selected torrents"),
422                                        [torrent size]]];
423       
424        NSString * hashString = [torrent hashString];
425        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
426                                        [NSString stringForFileSize: [torrent pieceSize]]]];
427        [fHashField setStringValue: hashString];
428        [fHashField setToolTip: hashString];
429        [fSecureField setStringValue: [torrent privateTorrent]
430                        ? NSLocalizedString(@"Private Torrent, PEX and DHT automatically disabled", "Inspector -> private torrent")
431                        : NSLocalizedString(@"Public Torrent", "Inspector -> private torrent")];
432       
433        NSString * commentString = [torrent comment];
434        [fCommentView setString: commentString];
435       
436        NSString * creatorString = [torrent creator];
437        [fCreatorField setStringValue: creatorString];
438        [fDateCreatedField setObjectValue: [torrent dateCreated]];
439       
440        [fDateAddedField setObjectValue: [torrent dateAdded]];
441       
442        [fRevealDataButton setHidden: NO];
443       
444        //allow these fields to be selected
445        [fHashField setSelectable: YES];
446        [fCommentView setSelectable: ![commentString isEqualToString: @""]];
447        [fCreatorField setSelectable: ![creatorString isEqualToString: @""]];
448        [fDataLocationField setSelectable: YES];
449       
450        //set pieces view
451        BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"];
452        [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE];
453        [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS];
454        [fPiecesControl setEnabled: YES];
455        [fPiecesView setTorrent: torrent];
456       
457        //get webseeds for table - if no webseeds for this torrent, clear the table
458        BOOL hasWebSeeds = [torrent webSeedCount] > 0;
459        [self setWebSeedTableHidden: !hasWebSeeds animate: YES];
460        if (!hasWebSeeds)
461        {
462            [fWebSeeds release];
463            fWebSeeds = nil;
464            [fWebSeedTable reloadData];
465        }
466       
467        [fTrackerTable setTorrent: torrent];
468        [fTrackerTable deselectAll: self];
469        [fTrackerAddRemoveControl setEnabled: YES forSegment: TRACKER_ADD_TAG];
470        [fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_REMOVE_TAG];
471
472        [fFileFilterField setEnabled: [torrent isFolder]];
473    }
474   
475    [fFileFilterField setStringValue: @""];
476   
477    //update stats and settings
478    [self updateInfoStats];
479    [self updateOptions];
480}
481
482- (void) updateInfoStats
483{
484    switch ([fTabMatrix selectedTag])
485    {
486        case TAB_INFO_TAG:
487            [self updateInfoGeneral];
488            break;
489        case TAB_ACTIVITY_TAG:
490            [self updateInfoActivity];
491            break;
492        case TAB_TRACKER_TAG:
493            [self updateInfoTracker];
494            break;
495        case TAB_PEERS_TAG:
496            [self updateInfoPeers];
497            break;
498        case TAB_FILES_TAG:
499            [self updateInfoFiles];
500            break;
501    }
502}
503
504- (void) updateOptions
505{
506    if ([fTorrents count] == 0)
507        return;
508   
509    //get bandwidth info
510    NSEnumerator * enumerator = [fTorrents objectEnumerator];
511    Torrent * torrent = [enumerator nextObject]; //first torrent
512   
513    NSInteger uploadUseSpeedLimit = [torrent usesSpeedLimit: YES] ? NSOnState : NSOffState,
514                uploadSpeedLimit = [torrent speedLimit: YES],
515                downloadUseSpeedLimit = [torrent usesSpeedLimit: NO] ? NSOnState : NSOffState,
516                downloadSpeedLimit = [torrent speedLimit: NO],
517                globalUseSpeedLimit = [torrent usesGlobalSpeedLimit] ? NSOnState : NSOffState;
518   
519    while ((torrent = [enumerator nextObject])
520            && (uploadUseSpeedLimit != NSMixedState || uploadSpeedLimit != INVALID
521                || downloadUseSpeedLimit != NSMixedState || downloadSpeedLimit != INVALID
522                || globalUseSpeedLimit != NSMixedState))
523    {
524        if (uploadUseSpeedLimit != NSMixedState && uploadUseSpeedLimit != ([torrent usesSpeedLimit: YES] ? NSOnState : NSOffState))
525            uploadUseSpeedLimit = NSMixedState;
526       
527        if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
528            uploadSpeedLimit = INVALID;
529       
530        if (downloadUseSpeedLimit != NSMixedState && downloadUseSpeedLimit != ([torrent usesSpeedLimit: NO] ? NSOnState : NSOffState))
531            downloadUseSpeedLimit = NSMixedState;
532       
533        if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
534            downloadSpeedLimit = INVALID;
535       
536        if (globalUseSpeedLimit != NSMixedState && globalUseSpeedLimit != ([torrent usesGlobalSpeedLimit] ? NSOnState : NSOffState))
537            globalUseSpeedLimit = NSMixedState;
538    }
539   
540    //set upload view
541    [fUploadLimitCheck setState: uploadUseSpeedLimit];
542    [fUploadLimitCheck setEnabled: YES];
543   
544    [fUploadLimitLabel setEnabled: uploadUseSpeedLimit == NSOnState];
545    [fUploadLimitField setEnabled: uploadUseSpeedLimit == NSOnState];
546    if (uploadSpeedLimit != INVALID)
547        [fUploadLimitField setIntValue: uploadSpeedLimit];
548    else
549        [fUploadLimitField setStringValue: @""];
550   
551    //set download view
552    [fDownloadLimitCheck setState: downloadUseSpeedLimit];
553    [fDownloadLimitCheck setEnabled: YES];
554   
555    [fDownloadLimitLabel setEnabled: downloadUseSpeedLimit == NSOnState];
556    [fDownloadLimitField setEnabled: downloadUseSpeedLimit == NSOnState];
557    if (downloadSpeedLimit != INVALID)
558        [fDownloadLimitField setIntValue: downloadSpeedLimit];
559    else
560        [fDownloadLimitField setStringValue: @""];
561   
562    //set global check
563    [fGlobalLimitCheck setState: globalUseSpeedLimit];
564    [fGlobalLimitCheck setEnabled: YES];
565   
566    //get ratio info
567    enumerator = [fTorrents objectEnumerator];
568    torrent = [enumerator nextObject]; //first torrent
569   
570    NSInteger checkRatio = [torrent ratioSetting];
571    CGFloat ratioLimit = [torrent ratioLimit];
572   
573    while ((torrent = [enumerator nextObject]) && (checkRatio != INVALID || ratioLimit != INVALID))
574    {
575        if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
576            checkRatio = INVALID;
577       
578        if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
579            ratioLimit = INVALID;
580    }
581   
582    //set ratio view
583    NSInteger index;
584    if (checkRatio == TR_RATIOLIMIT_SINGLE)
585        index = OPTION_POPUP_LIMIT;
586    else if (checkRatio == TR_RATIOLIMIT_UNLIMITED)
587        index = OPTION_POPUP_NO_LIMIT;
588    else if (checkRatio == TR_RATIOLIMIT_GLOBAL)
589        index = OPTION_POPUP_GLOBAL;
590    else
591        index = -1;
592    [fRatioPopUp selectItemAtIndex: index];
593    [fRatioPopUp setEnabled: YES];
594   
595    [fRatioLimitField setHidden: checkRatio != TR_RATIOLIMIT_SINGLE];
596    if (ratioLimit != INVALID)
597        [fRatioLimitField setFloatValue: ratioLimit];
598    else
599        [fRatioLimitField setStringValue: @""];
600   
601    //get priority info
602    enumerator = [fTorrents objectEnumerator];
603    torrent = [enumerator nextObject]; //first torrent
604   
605    NSInteger priority = [torrent priority];
606   
607    while ((torrent = [enumerator nextObject]) && priority != INVALID)
608    {
609        if (priority != INVALID && priority != [torrent priority])
610            priority = INVALID;
611    }
612   
613    //set priority view
614    if (priority == TR_PRI_HIGH)
615        index = OPTION_POPUP_PRIORITY_HIGH;
616    else if (priority == TR_PRI_NORMAL)
617        index = OPTION_POPUP_PRIORITY_NORMAL;
618    else if (priority == TR_PRI_LOW)
619        index = OPTION_POPUP_PRIORITY_LOW;
620    else
621        index = -1;
622    [fPriorityPopUp selectItemAtIndex: index];
623    [fPriorityPopUp setEnabled: YES];
624   
625    //get peer info
626    enumerator = [fTorrents objectEnumerator];
627    torrent = [enumerator nextObject]; //first torrent
628   
629    NSInteger maxPeers = [torrent maxPeerConnect];
630   
631    while ((torrent = [enumerator nextObject]))
632    {
633        if (maxPeers != [torrent maxPeerConnect])
634        {
635            maxPeers = INVALID;
636            break;
637        }
638    }
639   
640    //set peer view
641    [fPeersConnectField setEnabled: YES];
642    [fPeersConnectLabel setEnabled: YES];
643    if (maxPeers != INVALID)
644        [fPeersConnectField setIntValue: maxPeers];
645    else
646        [fPeersConnectField setStringValue: @""];
647}
648
649- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
650{
651    NSRect windowRect = [window frame];
652    windowRect.size.width = [window minSize].width;
653    return windowRect;
654}
655
656- (void) animationDidEnd: (NSAnimation *) animation
657{
658    if (animation == fWebSeedTableAnimation)
659    {
660        [fWebSeedTableAnimation release];
661        fWebSeedTableAnimation = nil;
662    }
663}
664
665- (NSSize) windowWillResize: (NSWindow *) window toSize: (NSSize) proposedFrameSize
666{
667    //this is an edge-case - just stop the animation (stopAnimation jumps to end frame)
668    if (fWebSeedTableAnimation)
669    {
670        [fWebSeedTableAnimation stopAnimation];
671        [fWebSeedTableAnimation release];
672        fWebSeedTableAnimation = nil;
673    }
674   
675    return proposedFrameSize;
676}
677
678- (void) windowWillClose: (NSNotification *) notification
679{
680    if ([NSApp isOnSnowLeopardOrBetter] && fCurrentTabTag == TAB_FILES_TAG
681        && ([QLPreviewPanelSL sharedPreviewPanelExists] && [[QLPreviewPanelSL sharedPreviewPanel] isVisible]))
682        [[QLPreviewPanelSL sharedPreviewPanel] reloadData];
683}
684
685- (void) setTab: (id) sender
686{
687    const NSInteger oldTabTag = fCurrentTabTag;
688    fCurrentTabTag = [fTabMatrix selectedTag];
689    if (fCurrentTabTag == oldTabTag)
690        return;
691   
692    [self updateInfoStats];
693   
694    //take care of old view
695    CGFloat oldHeight = 0;
696    NSString * oldResizeSaveKey = nil;
697    if (oldTabTag != INVALID)
698    {
699        //deselect old tab item
700        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
701       
702        switch (oldTabTag)
703        {
704            case TAB_ACTIVITY_TAG:
705                [fPiecesView clearView];
706                break;
707           
708            case TAB_TRACKER_TAG:
709                oldResizeSaveKey = @"InspectorContentHeightTracker";
710                break;
711           
712            case TAB_PEERS_TAG:
713                //if in the middle of animating, just stop and resize immediately
714                if (fWebSeedTableAnimation)
715                    [self setWebSeedTableHidden: !fWebSeeds animate: NO];
716               
717                [fPeers release];
718                fPeers = nil;
719                [fWebSeeds release];
720                fWebSeeds = nil;
721               
722                oldResizeSaveKey = @"InspectorContentHeightPeers";
723                break;
724           
725            case TAB_FILES_TAG:
726                oldResizeSaveKey = @"InspectorContentHeightFiles";
727                break;
728        }
729       
730        NSView * oldView = [self tabViewForTag: oldTabTag];
731        oldHeight = [oldView frame].size.height;
732        if (oldResizeSaveKey)
733            [[NSUserDefaults standardUserDefaults] setFloat: oldHeight forKey: oldResizeSaveKey];
734       
735        //remove old view
736        [oldView setHidden: YES];
737        [oldView removeFromSuperview];
738    }
739   
740    //set new tab item
741    NSView * view = [self tabViewForTag: fCurrentTabTag];
742   
743    NSString * resizeSaveKey = nil;
744    NSString * identifier, * title;
745    switch (fCurrentTabTag)
746    {
747        case TAB_INFO_TAG:
748            identifier = TAB_INFO_IDENT;
749            title = NSLocalizedString(@"General Info", "Inspector -> title");
750            break;
751        case TAB_ACTIVITY_TAG:
752            identifier = TAB_ACTIVITY_IDENT;
753            title = NSLocalizedString(@"Activity", "Inspector -> title");
754            break;
755        case TAB_TRACKER_TAG:
756            identifier = TAB_TRACKER_IDENT;
757            title = NSLocalizedString(@"Tracker", "Inspector -> title");
758            resizeSaveKey = @"InspectorContentHeightTracker";
759            break;
760        case TAB_PEERS_TAG:
761            identifier = TAB_PEERS_IDENT;
762            title = NSLocalizedString(@"Peers", "Inspector -> title");
763            resizeSaveKey = @"InspectorContentHeightPeers";
764            break;
765        case TAB_FILES_TAG:
766            identifier = TAB_FILES_IDENT;
767            title = NSLocalizedString(@"Files", "Inspector -> title");
768            resizeSaveKey = @"InspectorContentHeightFiles";
769            break;
770        case TAB_OPTIONS_TAG:
771            identifier = TAB_OPTIONS_IDENT;
772            title = NSLocalizedString(@"Options", "Inspector -> title");
773            break;
774        default:
775            NSAssert1(NO, @"Unknown info tab selected: %d", fCurrentTabTag);
776            return;
777    }
778   
779    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
780   
781    NSWindow * window = [self window];
782   
783    [window setTitle: [NSString stringWithFormat: @"%@ - %@", title, NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]];
784   
785    //selected tab item
786    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
787   
788    NSRect windowRect = [window frame], viewRect = [view frame];
789   
790    if (resizeSaveKey)
791    {
792        CGFloat height = [[NSUserDefaults standardUserDefaults] floatForKey: resizeSaveKey];
793        if (height != 0.0)
794            viewRect.size.height = MAX(height, TAB_MIN_HEIGHT);
795    }
796   
797    CGFloat difference = (viewRect.size.height - oldHeight) * [window userSpaceScaleFactor];
798    windowRect.origin.y -= difference;
799    windowRect.size.height += difference;
800   
801    if (resizeSaveKey)
802    {
803        if (!oldResizeSaveKey)
804        {
805            [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height - viewRect.size.height + TAB_MIN_HEIGHT)];
806            [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
807        }
808    }
809    else
810    {
811        [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height)];
812        [window setMaxSize: NSMakeSize(FLT_MAX, windowRect.size.height)];
813    }
814   
815    viewRect.size.width = windowRect.size.width;
816    [view setFrame: viewRect];
817   
818    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
819    [[window contentView] addSubview: view];
820    [view setHidden: NO];
821   
822    if ([NSApp isOnSnowLeopardOrBetter] && (fCurrentTabTag == TAB_FILES_TAG || oldTabTag == TAB_FILES_TAG)
823        && ([QLPreviewPanelSL sharedPreviewPanelExists] && [[QLPreviewPanelSL sharedPreviewPanel] isVisible]))
824        [[QLPreviewPanelSL sharedPreviewPanel] reloadData];
825}
826
827- (void) setNextTab
828{
829    NSInteger tag = [fTabMatrix selectedTag]+1;
830    if (tag >= [fTabMatrix numberOfColumns])
831        tag = 0;
832   
833    [fTabMatrix selectCellWithTag: tag];
834    [self setTab: nil];
835}
836
837- (void) setPreviousTab
838{
839    NSInteger tag = [fTabMatrix selectedTag]-1;
840    if (tag < 0)
841        tag = [fTabMatrix numberOfColumns]-1;
842   
843    [fTabMatrix selectCellWithTag: tag];
844    [self setTab: nil];
845}
846
847- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
848{
849    if (tableView == fPeerTable)
850        return fPeers ? [fPeers count] : 0;
851    else if (tableView == fWebSeedTable)
852        return fWebSeeds ? [fWebSeeds count] : 0;
853    else if (tableView == fTrackerTable)
854        return fTrackers ? [fTrackers count] : 0;
855    return 0;
856}
857
858- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (NSInteger) row
859{
860    if (tableView == fPeerTable)
861    {
862        NSString * ident = [column identifier];
863        NSDictionary * peer = [fPeers objectAtIndex: row];
864       
865        if ([ident isEqualToString: @"Encryption"])
866            return [[peer objectForKey: @"Encryption"] boolValue] ? [NSImage imageNamed: @"Lock.png"] : nil;
867        else if ([ident isEqualToString: @"Client"])
868            return [peer objectForKey: @"Client"];
869        else if  ([ident isEqualToString: @"Progress"])
870            return [peer objectForKey: @"Progress"];
871        else if ([ident isEqualToString: @"UL To"])
872        {
873            NSNumber * rate;
874            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
875        }
876        else if ([ident isEqualToString: @"DL From"])
877        {
878            NSNumber * rate;
879            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
880        }
881        else
882            return [peer objectForKey: @"IP"];
883    }
884    else if (tableView == fWebSeedTable)
885    {
886        NSString * ident = [column identifier];
887        NSDictionary * webSeed = [fWebSeeds objectAtIndex: row];
888       
889        if ([ident isEqualToString: @"DL From"])
890        {
891            NSNumber * rate;
892            return (rate = [webSeed objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
893        }
894        else
895            return [webSeed objectForKey: @"Address"];
896    }
897    else if (tableView == fTrackerTable)
898    {
899        id item = [fTrackers objectAtIndex: row];
900       
901        if ([item isKindOfClass: [NSNumber class]])
902            return [NSString stringWithFormat: NSLocalizedString(@"Tier %d", "Inspector -> tracker table"), [item integerValue]];
903        else
904            return item;
905    }
906    return nil;
907}
908
909- (NSCell *) tableView: (NSTableView *) tableView dataCellForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
910{
911    if (tableView == fTrackerTable)
912    {
913        const BOOL tracker = [[fTrackers objectAtIndex: row] isKindOfClass: [TrackerNode class]];
914        return tracker ? fTrackerCell : [tableColumn dataCellForRow: row];
915    }
916   
917    return nil;
918}
919
920- (CGFloat) tableView: (NSTableView *) tableView heightOfRow: (NSInteger) row
921{
922    if (tableView == fTrackerTable)
923    {
924        if ([[fTrackers objectAtIndex: row] isKindOfClass: [NSNumber class]])
925            return TRACKER_GROUP_SEPARATOR_HEIGHT;
926    }
927   
928    return [tableView rowHeight];
929}
930
931- (void) tableView: (NSTableView *) tableView willDisplayCell: (id) cell forTableColumn: (NSTableColumn *) tableColumn
932    row: (NSInteger) row
933{
934    if (tableView == fPeerTable)
935    {
936        NSString * ident = [tableColumn identifier];
937       
938        if  ([ident isEqualToString: @"Progress"])
939        {
940            NSDictionary * peer = [fPeers objectAtIndex: row];
941            [(PeerProgressIndicatorCell *)cell setSeed: [[peer objectForKey: @"Seed"] boolValue]];
942        }
943    }
944}
945
946- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
947{
948    if (tableView == fPeerTable)
949    {
950        if (fPeers)
951        {
952            NSArray * oldPeers = fPeers;
953            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
954            [oldPeers release];
955            [tableView reloadData];
956        }
957    }
958    else if (tableView == fWebSeedTable)
959    {
960        if (fWebSeeds)
961        {
962            NSArray * oldWebSeeds = fWebSeeds;
963            fWebSeeds = [[fWebSeeds sortedArrayUsingDescriptors: [fWebSeedTable sortDescriptors]] retain];
964            [oldWebSeeds release];
965            [tableView reloadData];
966        }
967    }
968    else;
969}
970
971- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (NSInteger) row
972{
973    return tableView == fTrackerTable;
974}
975
976- (void) tableViewSelectionDidChange: (NSNotification *) notification
977{
978    if ([notification object] == fTrackerTable)
979    {
980        NSInteger numSelected = [fTrackerTable numberOfSelectedRows];
981        [fTrackerAddRemoveControl setEnabled: numSelected > 0 forSegment: TRACKER_REMOVE_TAG];
982    }
983}
984
985- (BOOL) tableView: (NSTableView *) tableView isGroupRow: (NSInteger) row
986{
987    if (tableView == fTrackerTable)
988        return [[fTrackers objectAtIndex: row] isKindOfClass: [NSNumber class]];
989    return NO;
990}
991
992- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
993                tableColumn: (NSTableColumn *) column row: (NSInteger) row mouseLocation: (NSPoint) mouseLocation
994{
995    if (tableView == fPeerTable)
996    {
997        NSDictionary * peer = [fPeers objectAtIndex: row];
998        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 5];
999       
1000        CGFloat progress = [[peer objectForKey: @"Progress"] floatValue];
1001        NSString * progressString = [NSString localizedStringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
1002                                        "Inspector -> Peers tab -> table row tooltip"), progress * 100.0];
1003        if (progress < 1.0 && [[peer objectForKey: @"Seed"] boolValue])
1004            progressString = [progressString stringByAppendingFormat: @" (%@)", NSLocalizedString(@"Partial Seed",
1005                                "Inspector -> Peers tab -> table row tooltip")];
1006        [components addObject: progressString];
1007       
1008        if ([[peer objectForKey: @"Encryption"] boolValue])
1009            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
1010       
1011        NSString * portString;
1012        NSInteger port;
1013        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
1014            portString = [NSString stringWithFormat: @"%d", port];
1015        else
1016            portString = NSLocalizedString(@"N/A", "Inspector -> Peers tab -> table row tooltip");
1017        [components addObject: [NSString stringWithFormat: @"%@: %@", NSLocalizedString(@"Port",
1018            "Inspector -> Peers tab -> table row tooltip"), portString]];
1019       
1020        const NSInteger peerFrom = [[peer objectForKey: @"From"] integerValue];
1021        switch (peerFrom)
1022        {
1023            case TR_PEER_FROM_TRACKER:
1024                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
1025                break;
1026            case TR_PEER_FROM_INCOMING:
1027                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
1028                break;
1029            case TR_PEER_FROM_CACHE:
1030                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
1031                break;
1032            case TR_PEER_FROM_PEX:
1033                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
1034                break;
1035            case TR_PEER_FROM_DHT:
1036                [components addObject: NSLocalizedString(@"From: distributed hash table", "Inspector -> Peers tab -> table row tooltip")];
1037                break;
1038            default:
1039                NSAssert1(NO, @"Peer from unknown source: %d", peerFrom);
1040        }
1041       
1042        //determing status strings from flags
1043        NSMutableArray * statusArray = [NSMutableArray arrayWithCapacity: 6];
1044        NSString * flags = [peer objectForKey: @"Flags"];
1045       
1046        if ([flags rangeOfString: @"D"].location != NSNotFound)
1047            [statusArray addObject: NSLocalizedString(@"Currently downloading (interested and not choked)",
1048                "Inspector -> peer -> status")];
1049        if ([flags rangeOfString: @"d"].location != NSNotFound)
1050            [statusArray addObject: NSLocalizedString(@"You want to download, but peer does not want to send (interested and choked)",
1051                "Inspector -> peer -> status")];
1052        if ([flags rangeOfString: @"U"].location != NSNotFound)
1053            [statusArray addObject: NSLocalizedString(@"Currently uploading (interested and not choked)",
1054                "Inspector -> peer -> status")];
1055        if ([flags rangeOfString: @"u"].location != NSNotFound)
1056            [statusArray addObject: NSLocalizedString(@"Peer wants you to upload, but you do not want to (interested and choked)",
1057                "Inspector -> peer -> status")];
1058        if ([flags rangeOfString: @"K"].location != NSNotFound)
1059            [statusArray addObject: NSLocalizedString(@"Peer is unchoking you, but you are not interested",
1060                "Inspector -> peer -> status")];
1061        if ([flags rangeOfString: @"?"].location != NSNotFound)
1062            [statusArray addObject: NSLocalizedString(@"You unchoked the peer, but the peer is not interested",
1063                "Inspector -> peer -> status")];
1064       
1065        if ([statusArray count] > 0)
1066        {
1067            NSString * statusStrings = [statusArray componentsJoinedByString: @"\n\n"];
1068            [components addObject: [@"\n" stringByAppendingString: statusStrings]];
1069        }
1070       
1071        return [components componentsJoinedByString: @"\n"];
1072    }
1073    else if (tableView == fTrackerTable)
1074    {
1075        id node = [fTrackers objectAtIndex: row];
1076        if (![node isKindOfClass: [NSNumber class]])
1077            return [(TrackerNode *)node fullAnnounceAddress];
1078    }
1079   
1080    return nil;
1081}
1082
1083- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
1084    row: (NSInteger) row
1085{
1086    if (tableView != fTrackerTable)
1087        return;
1088   
1089    Torrent * torrent= [fTorrents objectAtIndex: 0];
1090    if (![torrent addTrackerToNewTier: object])
1091        NSBeep();
1092   
1093    //reset table with either new or old value
1094    [fTrackers release];
1095    fTrackers = [[torrent allTrackerStats] retain];
1096   
1097    [fTrackerTable setTrackers: fTrackers];
1098    [fTrackerTable reloadData];
1099    [fTrackerTable deselectAll: self];
1100   
1101    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil]; //incase sort by tracker
1102}
1103
1104- (void) addRemoveTracker: (id) sender
1105{
1106    //don't allow add/remove when currently adding - it leads to weird results
1107    if ([fTrackerTable editedRow] != -1)
1108        return;
1109   
1110    if ([[sender cell] tagForSegment: [sender selectedSegment]] == TRACKER_REMOVE_TAG)
1111        [self removeTrackers];
1112    else
1113        [self addTrackers];
1114}
1115
1116#warning is editing trackers needed?
1117/*- (BOOL) tableView: (NSTableView *) tableView shouldEditTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
1118{
1119    if (tableView != fTrackerTable)
1120        return NO;
1121   
1122    NSUInteger i;
1123    for (i = row-1; ![[fTrackers objectAtIndex: i] isKindOfClass: [NSNumber class]]; i--);
1124   
1125    return [[fTrackers objectAtIndex: i] intValue] == 0;
1126}*/
1127
1128- (NSArray *) quickLookURLs
1129{
1130    FileOutlineView * fileOutlineView = [fFileController outlineView];
1131    Torrent * torrent = [fTorrents objectAtIndex: 0];
1132    NSString * folder = [torrent downloadFolder];
1133    NSIndexSet * indexes = [fileOutlineView selectedRowIndexes];
1134    NSMutableArray * urlArray = [NSMutableArray arrayWithCapacity: [indexes count]];
1135   
1136    for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
1137    {
1138        FileListNode * item = [fileOutlineView itemAtRow: i];
1139        if ([self canQuickLookFile: item])
1140            [urlArray addObject: [NSURL fileURLWithPath: [folder stringByAppendingPathComponent: [item fullPath]]]];
1141    }
1142   
1143    return urlArray;
1144}
1145
1146- (BOOL) canQuickLook
1147{
1148    if (fCurrentTabTag != TAB_FILES_TAG || ![[self window] isVisible] || [fTorrents count] != 1 || ![NSApp isOnSnowLeopardOrBetter])
1149        return NO;
1150   
1151    Torrent * torrent = [fTorrents objectAtIndex: 0];
1152    if (![torrent isFolder])
1153        return NO;
1154   
1155    FileOutlineView * fileOutlineView = [fFileController outlineView];
1156    NSIndexSet * indexes = [fileOutlineView selectedRowIndexes];
1157   
1158    for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
1159        if ([self canQuickLookFile: [fileOutlineView itemAtRow: i]])
1160            return YES;
1161   
1162    return NO;
1163}
1164
1165#warning uncomment (in header too)
1166- (NSRect) quickLookSourceFrameForPreviewItem: (id /*<QLPreviewItem>*/) item
1167{
1168    FileOutlineView * fileOutlineView = [fFileController outlineView];
1169   
1170    NSString * fullPath = [(NSURL *) item path];
1171    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
1172    NSRange visibleRows = [fileOutlineView rowsInRect: [fileOutlineView bounds]];
1173   
1174    for (NSUInteger row = visibleRows.location; row < NSMaxRange(visibleRows); row++)
1175    {
1176        FileListNode * rowItem = [fileOutlineView itemAtRow: row];
1177        if ([[folder stringByAppendingPathComponent: [rowItem fullPath]] isEqualToString: fullPath])
1178        {
1179            NSRect frame = [fileOutlineView iconRectForRow: row];
1180            frame.origin = [fileOutlineView convertPoint: frame.origin toView: nil];
1181            frame.origin = [[self window] convertBaseToScreen: frame.origin];
1182            frame.origin.y -= frame.size.height;
1183            return frame;
1184        }
1185    }
1186   
1187    return NSZeroRect;
1188}
1189
1190- (void) setPiecesView: (id) sender
1191{
1192    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
1193}
1194
1195- (void) setPiecesViewForAvailable: (BOOL) available
1196{
1197    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
1198    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
1199   
1200    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
1201    [fPiecesView updateView];
1202}
1203
1204- (void) revealDataFile: (id) sender
1205{
1206    if ([fTorrents count] > 0)
1207    {
1208        Torrent * torrent = [fTorrents objectAtIndex: 0];
1209        if ([NSApp isOnSnowLeopardOrBetter])
1210        {
1211            NSURL * file = [NSURL fileURLWithPath: [torrent dataLocation]];
1212            [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs: [NSArray arrayWithObject: file]];
1213        }
1214        else
1215            [[NSWorkspace sharedWorkspace] selectFile: [torrent dataLocation] inFileViewerRootedAtPath: nil];
1216    }
1217}
1218
1219- (void) setFileFilterText: (id) sender
1220{
1221    [fFileController setFilterText: [sender stringValue]];
1222}
1223
1224- (void) setUseSpeedLimit: (id) sender
1225{
1226    const BOOL upload = sender == fUploadLimitCheck;
1227   
1228    if ([sender state] == NSMixedState)
1229        [sender setState: NSOnState];
1230    const BOOL limit = [sender state] == NSOnState;
1231   
1232    for (Torrent * torrent in fTorrents)
1233        [torrent setUseSpeedLimit: limit upload: upload];
1234   
1235    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
1236    [field setEnabled: limit];
1237    if (limit)
1238    {
1239        [field selectText: self];
1240        [[self window] makeKeyAndOrderFront: self];
1241    }
1242   
1243    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
1244    [label setEnabled: limit];
1245}
1246
1247- (void) setUseGlobalSpeedLimit: (id) sender
1248{
1249    if ([sender state] == NSMixedState)
1250        [sender setState: NSOnState];
1251    const BOOL limit = [sender state] == NSOnState;
1252   
1253    for (Torrent * torrent in fTorrents)
1254        [torrent setUseGlobalSpeedLimit: limit];
1255}
1256
1257- (void) setSpeedLimit: (id) sender
1258{
1259    const BOOL upload = sender == fUploadLimitField;
1260    const NSInteger limit = [sender intValue];
1261   
1262    for (Torrent * torrent in fTorrents)
1263        [torrent setSpeedLimit: limit upload: upload];
1264}
1265
1266- (void) setRatioSetting: (id) sender
1267{
1268    NSInteger setting;
1269    bool single = NO;
1270    switch ([sender indexOfSelectedItem])
1271    {
1272        case OPTION_POPUP_LIMIT:
1273            setting = TR_RATIOLIMIT_SINGLE;
1274            single = YES;
1275            break;
1276        case OPTION_POPUP_NO_LIMIT:
1277            setting = TR_RATIOLIMIT_UNLIMITED;
1278            break;
1279        case OPTION_POPUP_GLOBAL:
1280            setting = TR_RATIOLIMIT_GLOBAL;
1281            break;
1282        default:
1283            NSAssert1(NO, @"Unknown option selected in ratio popup: %d", [sender indexOfSelectedItem]);
1284            return;
1285    }
1286   
1287    for (Torrent * torrent in fTorrents)
1288        [torrent setRatioSetting: setting];
1289   
1290    [fRatioLimitField setHidden: !single];
1291    if (single)
1292    {
1293        [fRatioLimitField selectText: self];
1294        [[self window] makeKeyAndOrderFront: self];
1295    }
1296}
1297
1298- (void) setRatioLimit: (id) sender
1299{
1300    CGFloat limit = [sender floatValue];
1301   
1302    for (Torrent * torrent in fTorrents)
1303        [torrent setRatioLimit: limit];
1304}
1305
1306- (void) setPriority: (id) sender
1307{
1308    tr_priority_t priority;
1309    switch ([sender indexOfSelectedItem])
1310    {
1311        case OPTION_POPUP_PRIORITY_HIGH:
1312            priority = TR_PRI_HIGH;
1313            break;
1314        case OPTION_POPUP_PRIORITY_NORMAL:
1315            priority = TR_PRI_NORMAL;
1316            break;
1317        case OPTION_POPUP_PRIORITY_LOW:
1318            priority = TR_PRI_LOW;
1319            break;
1320        default:
1321            NSAssert1(NO, @"Unknown option selected in priority popup: %d", [sender indexOfSelectedItem]);
1322            return;
1323    }
1324   
1325    for (Torrent * torrent in fTorrents)
1326        [torrent setPriority: priority];
1327   
1328    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1329}
1330
1331- (void) setPeersConnectLimit: (id) sender
1332{
1333    NSInteger limit = [sender intValue];
1334   
1335    for (Torrent * torrent in fTorrents)
1336        [torrent setMaxPeerConnect: limit];
1337}
1338
1339- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
1340{
1341    [fInitialString release];
1342    fInitialString = [[control stringValue] retain];
1343   
1344    return YES;
1345}
1346
1347- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
1348{
1349    NSBeep();
1350    if (fInitialString)
1351    {
1352        [control setStringValue: fInitialString];
1353        [fInitialString release];
1354        fInitialString = nil;
1355    }
1356    return NO;
1357}
1358
1359@end
1360
1361@implementation InfoWindowController (Private)
1362
1363- (void) updateInfoGeneral
1364{   
1365    if ([fTorrents count] != 1)
1366        return;
1367   
1368    Torrent * torrent = [fTorrents objectAtIndex: 0];
1369   
1370    NSString * location = [torrent dataLocation];
1371    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1372    [fDataLocationField setToolTip: location];
1373}
1374
1375- (void) updateInfoActivity
1376{
1377    NSInteger numberSelected = [fTorrents count];
1378    if (numberSelected == 0)
1379        return;
1380   
1381    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1382    NSDate * lastActivity = nil;
1383    for (Torrent * torrent in fTorrents)
1384    {
1385        have += [torrent haveTotal];
1386        haveVerified += [torrent haveVerified];
1387        downloadedTotal += [torrent downloadedTotal];
1388        uploadedTotal += [torrent uploadedTotal];
1389        failedHash += [torrent failedHash];
1390       
1391        NSDate * nextLastActivity;
1392        if ((nextLastActivity = [torrent dateActivity]))
1393            lastActivity = lastActivity ? [lastActivity laterDate: nextLastActivity] : nextLastActivity;
1394    }
1395   
1396    if (have == 0)
1397        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1398    else
1399    {
1400        NSString * verifiedString = [NSString stringWithFormat: NSLocalizedString(@"%@ verified", "Inspector -> Activity tab -> have"),
1401                                        [NSString stringForFileSize: haveVerified]];
1402        if (have == haveVerified)
1403            [fHaveField setStringValue: verifiedString];
1404        else
1405            [fHaveField setStringValue: [NSString stringWithFormat: @"%@ (%@)", [NSString stringForFileSize: have], verifiedString]];
1406    }
1407   
1408    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1409    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1410    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1411   
1412    [fDateActivityField setObjectValue: lastActivity];
1413   
1414    if (numberSelected == 1)
1415    {
1416        Torrent * torrent = [fTorrents objectAtIndex: 0];
1417       
1418        [fStateField setStringValue: [torrent stateString]];
1419       
1420        if ([torrent isFolder])
1421            [fProgressField setStringValue: [NSString localizedStringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1422                "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1423        else
1424            [fProgressField setStringValue: [NSString localizedStringWithFormat: @"%.2f%%", 100.0 * [torrent progress]]];
1425           
1426        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1427        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1428       
1429        NSString * errorMessage = [torrent errorMessage];
1430        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1431        {
1432            [fErrorMessageView setString: errorMessage];
1433            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1434        }
1435       
1436        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1437       
1438        [fPiecesView updateView];
1439    }
1440    else if (numberSelected > 1)
1441    {
1442        [fRatioField setStringValue: [NSString stringForRatio: tr_getRatio(uploadedTotal, downloadedTotal)]];
1443    }
1444    else;
1445}
1446
1447- (void) updateInfoTracker
1448{
1449    if ([fTorrents count] != 1)
1450        return;
1451    Torrent * torrent = [fTorrents objectAtIndex: 0];
1452   
1453    //get updated tracker stats
1454    if ([fTrackerTable editedRow] == -1)
1455    {
1456        [fTrackers release];
1457        fTrackers = [[torrent allTrackerStats] retain];
1458       
1459        [fTrackerTable setTrackers: fTrackers];
1460        [fTrackerTable reloadData];
1461    }
1462    else
1463    {
1464        if ([NSApp isOnSnowLeopardOrBetter])
1465        {
1466            NSIndexSet * addedIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange([fTrackers count]-2, 2)];
1467            NSArray * tierAndTrackerBeingAdded = [fTrackers objectsAtIndexes: addedIndexes];
1468           
1469            [fTrackers release];
1470            fTrackers = [[torrent allTrackerStats] retain];
1471            [fTrackers addObjectsFromArray: tierAndTrackerBeingAdded];
1472           
1473            [fTrackerTable setTrackers: fTrackers];
1474           
1475            NSIndexSet * updateIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTrackers count]-2)],
1476                    * columnIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [[fTrackerTable tableColumns] count])];
1477            [fTrackerTable reloadDataForRowIndexes: updateIndexes columnIndexes: columnIndexes];
1478        }
1479    }
1480}
1481
1482- (void) updateInfoPeers
1483{
1484    if ([fTorrents count] != 1)
1485        return;
1486    Torrent * torrent = [fTorrents objectAtIndex: 0];
1487   
1488    if ([torrent isActive])
1489    {
1490        NSInteger total = [torrent totalPeersConnected];
1491        NSString * connected = [NSString stringWithFormat:
1492                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1493       
1494        if (total > 0)
1495        {
1496            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 5];
1497            NSInteger count;
1498            if ((count = [torrent totalPeersTracker]) > 0)
1499                [components addObject: [NSString stringWithFormat:
1500                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1501            if ((count = [torrent totalPeersIncoming]) > 0)
1502                [components addObject: [NSString stringWithFormat:
1503                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1504            if ((count = [torrent totalPeersCache]) > 0)
1505                [components addObject: [NSString stringWithFormat:
1506                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1507            if ((count = [torrent totalPeersPex]) > 0)
1508                [components addObject: [NSString stringWithFormat:
1509                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1510            if ((count = [torrent totalPeersDHT]) > 0)
1511                [components addObject: [NSString stringWithFormat:
1512                                        NSLocalizedString(@"%d DHT", "Inspector -> Peers tab -> peers"), count]];
1513           
1514            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1515        }
1516       
1517        [fConnectedPeersField setStringValue: connected];
1518       
1519        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1520        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1521    }
1522    else
1523    {
1524        [fConnectedPeersField setStringValue: @""];
1525        [fDownloadingFromField setStringValue: @""];
1526        [fUploadingToField setStringValue: @""];
1527    }
1528   
1529    [fKnownField setIntValue: [torrent totalPeersKnown]];
1530   
1531    [fPeers release];
1532    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1533    [fPeerTable reloadData];
1534   
1535    if ([torrent webSeedCount] > 0)
1536    {
1537        [fWebSeeds release];
1538        fWebSeeds = [[[torrent webSeeds] sortedArrayUsingDescriptors: [fWebSeedTable sortDescriptors]] retain];
1539        [fWebSeedTable reloadData];
1540    }
1541}
1542
1543- (void) updateInfoFiles
1544{
1545    if ([fTorrents count] == 1)
1546        [fFileController reloadData];
1547}
1548
1549- (NSView *) tabViewForTag: (NSInteger) tag
1550{
1551    switch (tag)
1552    {
1553        case TAB_INFO_TAG:
1554            return fInfoView;
1555        case TAB_ACTIVITY_TAG:
1556            return fActivityView;
1557        case TAB_TRACKER_TAG:
1558            return fTrackerView;
1559        case TAB_PEERS_TAG:
1560            return fPeersView;
1561        case TAB_FILES_TAG:
1562            return fFilesView;
1563        case TAB_OPTIONS_TAG:
1564            return fOptionsView;
1565        default:
1566            NSAssert1(NO, @"Unknown tab view for tag: %d", tag);
1567            return nil;
1568    }
1569}
1570
1571- (void) setWebSeedTableHidden: (BOOL) hide animate: (BOOL) animate
1572{
1573    if (fCurrentTabTag != TAB_PEERS_TAG || ![[self window] isVisible])
1574        animate = NO;
1575   
1576    if (fWebSeedTableAnimation)
1577    {
1578        [fWebSeedTableAnimation stopAnimation];
1579        [fWebSeedTableAnimation release];
1580        fWebSeedTableAnimation = nil;
1581    }
1582   
1583    NSRect webSeedFrame = [[fWebSeedTable enclosingScrollView] frame];
1584    NSRect peerFrame = [[fPeerTable enclosingScrollView] frame];
1585   
1586    if (hide)
1587    {
1588        CGFloat webSeedFrameMaxY = NSMaxY(webSeedFrame);
1589        webSeedFrame.size.height = 0;
1590        webSeedFrame.origin.y = webSeedFrameMaxY;
1591       
1592        peerFrame.size.height = webSeedFrameMaxY - peerFrame.origin.y;
1593    }
1594    else
1595    {
1596        webSeedFrame.origin.y -= fWebSeedTableHeight - webSeedFrame.size.height;
1597        webSeedFrame.size.height = fWebSeedTableHeight;
1598       
1599        peerFrame.size.height = (webSeedFrame.origin.y - fSpaceBetweenWebSeedAndPeer) - peerFrame.origin.y;
1600    }
1601   
1602    [[fWebSeedTable enclosingScrollView] setHidden: NO]; //this is needed for some reason
1603   
1604    //actually resize tables
1605    if (animate)
1606    {
1607        NSDictionary * webSeedDict = [NSDictionary dictionaryWithObjectsAndKeys:
1608                                    [fWebSeedTable enclosingScrollView], NSViewAnimationTargetKey,
1609                                    [NSValue valueWithRect: [[fWebSeedTable enclosingScrollView] frame]], NSViewAnimationStartFrameKey,
1610                                    [NSValue valueWithRect: webSeedFrame], NSViewAnimationEndFrameKey, nil],
1611                    * peerDict = [NSDictionary dictionaryWithObjectsAndKeys:
1612                                    [fPeerTable enclosingScrollView], NSViewAnimationTargetKey,
1613                                    [NSValue valueWithRect: [[fPeerTable enclosingScrollView] frame]], NSViewAnimationStartFrameKey,
1614                                    [NSValue valueWithRect: peerFrame], NSViewAnimationEndFrameKey, nil];
1615       
1616        fWebSeedTableAnimation = [[NSViewAnimation alloc] initWithViewAnimations:
1617                                        [NSArray arrayWithObjects: webSeedDict, peerDict, nil]];
1618        [fWebSeedTableAnimation setDuration: 0.125];
1619        [fWebSeedTableAnimation setAnimationBlockingMode: NSAnimationNonblocking];
1620        [fWebSeedTableAnimation setDelegate: self];
1621       
1622        [fWebSeedTableAnimation startAnimation];
1623    }
1624    else
1625    {
1626        [[fWebSeedTable enclosingScrollView] setFrame: webSeedFrame];
1627        [[fPeerTable enclosingScrollView] setFrame: peerFrame];
1628    }
1629}
1630
1631- (NSArray *) peerSortDescriptors
1632{
1633    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1634   
1635    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1636    BOOL useSecond = YES, asc = YES;
1637    if ([oldDescriptors count] > 0)
1638    {
1639        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1640        [descriptors addObject: descriptor];
1641       
1642        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1643            asc = [descriptor ascending];
1644    }
1645   
1646    //sort by IP after primary sort
1647    if (useSecond)
1648    {
1649        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1650                                                                        selector: @selector(compareNumeric:)];
1651        [descriptors addObject: secondDescriptor];
1652        [secondDescriptor release];
1653    }
1654   
1655    return descriptors;
1656}
1657
1658- (BOOL) canQuickLookFile: (FileListNode *) item
1659{
1660    Torrent * torrent = [fTorrents objectAtIndex: 0];
1661   
1662    if (![[NSFileManager defaultManager] fileExistsAtPath: [[torrent downloadFolder] stringByAppendingPathComponent: [item fullPath]]])
1663        return NO;
1664   
1665    return [item isFolder] || [torrent fileProgress: item] >= 1.0;
1666}
1667
1668#warning doesn't like blank addresses
1669- (void) addTrackers
1670{
1671    [[self window] makeKeyWindow];
1672   
1673    [fTrackers addObject: [NSNumber numberWithInt: [(TrackerNode *)[fTrackers lastObject] tier]+1]];
1674    [fTrackers addObject: @""];
1675   
1676    [fTrackerTable setTrackers: fTrackers];
1677    [fTrackerTable reloadData];
1678    [fTrackerTable selectRowIndexes: [NSIndexSet indexSetWithIndex: [fTrackers count]-1] byExtendingSelection: NO];
1679    [fTrackerTable editColumn: [fTrackerTable columnWithIdentifier: @"Tracker"] row: [fTrackers count]-1 withEvent: nil select: YES];
1680}
1681
1682- (void) removeTrackers
1683{
1684    const NSInteger oldCount = [fTrackers count] - [(TrackerNode *)[fTrackers lastObject] tier];
1685    NSMutableSet * addresses = [NSMutableSet setWithCapacity: oldCount];
1686   
1687    NSIndexSet * indexes = [fTrackerTable selectedRowIndexes];
1688    for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
1689    {
1690        id item = [fTrackers objectAtIndex: i];
1691        if ([item isKindOfClass: [NSNumber class]])
1692        {
1693            for (++i; i < [fTrackers count] && ![[fTrackers objectAtIndex: i] isKindOfClass: [NSNumber class]]; ++i)
1694                [addresses addObject: [[fTrackers objectAtIndex: i] fullAnnounceAddress]];
1695            --i;
1696        }
1697        else
1698            [addresses addObject: [(TrackerNode *)item fullAnnounceAddress]];
1699    }
1700   
1701    if (oldCount == [addresses count])
1702    {
1703        NSBeep();
1704        return;
1705    }
1706   
1707    if ([[NSUserDefaults standardUserDefaults] boolForKey: @"WarningRemoveTrackers"])
1708    {
1709        NSAlert * alert = [[NSAlert alloc] init];
1710       
1711        if ([addresses count] > 1)
1712        {
1713            [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove %d trackers?",
1714                                                                "Remove trackers alert -> title"), [addresses count]]];
1715            [alert setInformativeText: NSLocalizedString(@"Once removed, Transmission will no longer attempt to contact them."
1716                                        " This cannot be undone.", "Remove trackers alert -> message")];
1717        }
1718        else
1719        {
1720            [alert setMessageText: NSLocalizedString(@"Are you sure you want to remove this tracker?", "Remove trackers alert -> title")];
1721            [alert setInformativeText: NSLocalizedString(@"Once removed, Transmission will no longer attempt to contact it."
1722                                        " This cannot be undone.", "Remove trackers alert -> message")];
1723        }
1724       
1725        [alert addButtonWithTitle: NSLocalizedString(@"Remove", "Remove trackers alert -> button")];
1726        [alert addButtonWithTitle: NSLocalizedString(@"Cancel", "Remove trackers alert -> button")];
1727       
1728        [alert setShowsSuppressionButton: YES];
1729
1730        NSInteger result = [alert runModal];
1731        if ([[alert suppressionButton] state] == NSOnState)
1732            [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @"WarningRemoveTrackers"];
1733        [alert release];
1734       
1735        if (result != NSAlertFirstButtonReturn)
1736            return;
1737    }
1738   
1739    Torrent * torrent = [fTorrents objectAtIndex: 0];
1740    [torrent removeTrackersWithAnnounceAddresses: addresses];
1741   
1742    //reset table with either new or old value
1743    [fTrackers release];
1744    fTrackers = [[torrent allTrackerStats] retain];
1745   
1746    [fTrackerTable setTrackers: fTrackers];
1747    [fTrackerTable reloadData];
1748    [fTrackerTable deselectAll: self];
1749   
1750    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil]; //incase sort by tracker
1751}
1752
1753@end
Note: See TracBrowser for help on using the repository browser.