source: trunk/macosx/InfoWindowController.m @ 9184

Last change on this file since 9184 was 9184, checked in by livings124, 8 years ago

always get favicons from http://

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