source: trunk/macosx/InfoWindowController.m @ 9297

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

add some more switch statement asserts

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