source: trunk/macosx/InfoWindowController.m @ 5297

Last change on this file since 5297 was 5297, checked in by livings124, 14 years ago

more string folding

  • Property svn:keywords set to Date Rev Author Id
File size: 46.7 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 5297 2008-03-18 22:11:43Z livings124 $
3 *
4 * Copyright (c) 2006-2008 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 "InfoTabButtonCell.h"
27#import "NSApplicationAdditions.h"
28#import "NSStringAdditions.h"
29
30#define TAB_INFO_IDENT @"Info"
31#define TAB_ACTIVITY_IDENT @"Activity"
32#define TAB_TRACKER_IDENT @"Tracker"
33#define TAB_PEERS_IDENT @"Peers"
34#define TAB_FILES_IDENT @"Files"
35#define TAB_OPTIONS_IDENT @"Options"
36
37#define TAB_MIN_HEIGHT 250
38
39#define PIECES_CONTROL_PROGRESS 0
40#define PIECES_CONTROL_AVAILABLE 1
41
42#define OPTION_POPUP_GLOBAL 0
43#define OPTION_POPUP_NO_LIMIT 1
44#define OPTION_POPUP_LIMIT 2
45
46#define INVALID -99
47
48typedef enum
49{
50    TAB_INFO_TAG = 0,
51    TAB_ACTIVITY_TAG = 1,
52    TAB_TRACKER_TAG = 2,
53    TAB_PEERS_TAG = 3,
54    TAB_FILES_TAG = 4,
55    TAB_OPTIONS_TAG = 5
56} tabTag;
57
58@interface InfoWindowController (Private)
59
60- (void) updateInfoGeneral;
61- (void) updateInfoActivity;
62- (void) updateInfoTracker;
63- (void) updateInfoPeers;
64- (void) updateInfoFiles;
65
66- (NSView *) tabViewForTag: (int) tag;
67- (NSArray *) peerSortDescriptors;
68
69@end
70
71@implementation InfoWindowController
72
73- (id) init
74{
75    return [super initWithWindowNibName: @"InfoWindow"];
76}
77
78- (void) awakeFromNib
79{
80    //window location and size
81    NSPanel * window = (NSPanel *)[self window];
82   
83    float windowHeight = [window frame].size.height;
84   
85    [window setFrameAutosaveName: @"InspectorWindow"];
86    [window setFrameUsingName: @"InspectorWindow"];
87   
88    NSRect windowRect = [window frame];
89    windowRect.origin.y -= windowHeight - windowRect.size.height;
90    windowRect.size.height = windowHeight;
91    [window setFrame: windowRect display: NO];
92   
93    [window setBecomesKeyOnlyIfNeeded: YES];
94   
95    //set tab images and tooltips
96    [[fTabMatrix cellWithTag: TAB_INFO_TAG] setIcon: [NSImage imageNamed: @"InfoGeneral.png"]];
97    [[fTabMatrix cellWithTag: TAB_ACTIVITY_TAG] setIcon: [NSImage imageNamed: @"InfoActivity.png"]];
98    [[fTabMatrix cellWithTag: TAB_TRACKER_TAG] setIcon: [NSImage imageNamed: @"InfoTracker.png"]];
99    [[fTabMatrix cellWithTag: TAB_PEERS_TAG] setIcon: [NSImage imageNamed: @"InfoPeers.png"]];
100    [[fTabMatrix cellWithTag: TAB_FILES_TAG] setIcon: [NSImage imageNamed: @"InfoFiles.png"]];
101    [[fTabMatrix cellWithTag: TAB_OPTIONS_TAG] setIcon: [NSImage imageNamed: @"InfoOptions.png"]];
102   
103    //set selected tab
104    fCurrentTabTag = INVALID;
105    NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"];
106    int tag;
107    if ([identifier isEqualToString: TAB_INFO_IDENT])
108        tag = TAB_INFO_TAG;
109    else if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
110        tag = TAB_ACTIVITY_TAG;
111    else if ([identifier isEqualToString: TAB_TRACKER_IDENT])
112        tag = TAB_TRACKER_TAG;
113    else if ([identifier isEqualToString: TAB_PEERS_IDENT])
114        tag = TAB_PEERS_TAG;
115    else if ([identifier isEqualToString: TAB_FILES_IDENT])
116        tag = TAB_FILES_TAG;
117    else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
118        tag = TAB_OPTIONS_TAG;
119    else //safety
120    {
121        [[NSUserDefaults standardUserDefaults] setObject: TAB_INFO_IDENT forKey: @"InspectorSelected"];
122        tag = TAB_INFO_TAG;
123    }
124    [fTabMatrix selectCellWithTag: tag];
125    [self setTab: nil];
126   
127    //reset images for reveal buttons, since the images are also used in the main table
128    NSImage * revealOn = [[NSImage imageNamed: @"RevealOn.png"] copy],
129            * revealOff = [[NSImage imageNamed: @"RevealOff.png"] copy];
130    [revealOn setFlipped: NO];
131    [revealOff setFlipped: NO];
132   
133    [fRevealDataButton setImage: revealOff];
134    [fRevealDataButton setAlternateImage: revealOn];
135    [fRevealTorrentButton setImage: revealOff];
136    [fRevealTorrentButton setAlternateImage: revealOn];
137   
138    [revealOn release];
139    [revealOff release];
140   
141    //initially sort peer table by IP
142    if ([[fPeerTable sortDescriptors] count] == 0)
143        [fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
144                                            sortDescriptorPrototype]]];
145   
146    //set table header tool tips
147    if ([NSApp isOnLeopardOrBetter])
148    {
149        [[fPeerTable tableColumnWithIdentifier: @"Encryption"] setHeaderToolTip: NSLocalizedString(@"Encrypted Connection",
150                                                                            "inspector -> peer table -> header tool tip")];
151        [[fPeerTable tableColumnWithIdentifier: @"Progress"] setHeaderToolTip: NSLocalizedString(@"Available",
152                                                                            "inspector -> peer table -> header tool tip")];
153        [[fPeerTable tableColumnWithIdentifier: @"UL To"] setHeaderToolTip: NSLocalizedString(@"Uploading To Peer",
154                                                                            "inspector -> peer table -> header tool tip")];
155        [[fPeerTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Peer",
156                                                                            "inspector -> peer table -> header tool tip")];                                                             
157    }
158   
159    //set blank inspector
160    [self setInfoForTorrents: [NSArray array]];
161   
162    //allow for update notifications
163    NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
164    [nc addObserver: self selector: @selector(updateInfoStats) name: @"UpdateStats" object: nil];
165    [nc addObserver: self selector: @selector(updateOptions) name: @"UpdateOptions" object: nil];
166}
167
168- (void) dealloc
169{
170    //save resizeable view height
171    NSString * resizeSaveKey = nil;
172    switch (fCurrentTabTag)
173    {
174        case TAB_TRACKER_TAG:
175            resizeSaveKey = @"InspectorContentHeightTracker";
176            break;
177        case TAB_PEERS_TAG:
178            resizeSaveKey = @"InspectorContentHeightPeers";
179            break;
180        case TAB_FILES_TAG:
181            resizeSaveKey = @"InspectorContentHeightFiles";
182            break;
183    }
184    if (resizeSaveKey)
185        [[NSUserDefaults standardUserDefaults] setFloat: [[self tabViewForTag: fCurrentTabTag] frame].size.height forKey: resizeSaveKey];
186   
187    [[NSNotificationCenter defaultCenter] removeObserver: self];
188   
189    [fTorrents release];
190    [fPeers release];
191    [fTrackers release];
192   
193    [super dealloc];
194}
195
196- (void) setInfoForTorrents: (NSArray *) torrents
197{
198    if (fTorrents && [fTorrents isEqualToArray: torrents])
199        return;
200   
201    [fTorrents release];
202    fTorrents = [torrents retain];
203
204    int numberSelected = [fTorrents count];
205    if (numberSelected != 1)
206    {
207        if (numberSelected > 0)
208        {
209            [fImageView setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter]
210                                    ? NSImageNameMultipleDocuments : @"NSApplicationIcon"]];
211           
212            [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d Torrents Selected",
213                                            "Inspector -> above tabs -> selected torrents"), numberSelected]];
214       
215            uint64_t size = 0;
216            int fileCount = 0;
217            NSEnumerator * enumerator = [torrents objectEnumerator];
218            Torrent * torrent;
219            while ((torrent = [enumerator nextObject]))
220            {
221                size += [torrent size];
222                fileCount += [torrent fileCount];
223            }
224           
225            [fBasicInfoField setStringValue: [NSString stringWithFormat: @"%@, %@",
226                [NSString stringWithFormat: NSLocalizedString(@"%d Files", "Inspector -> selected torrents"), fileCount],
227                [NSString stringWithFormat: NSLocalizedString(@"%@ Total", "Inspector -> selected torrents"),
228                [NSString stringForFileSize: size]]]];
229            [fBasicInfoField setToolTip: [NSString stringWithFormat: NSLocalizedString(@"%u bytes",
230                                            "Inspector -> above tabs -> selected torrents"), size]];
231        }
232        else
233        {
234            [fImageView setImage: [NSImage imageNamed: @"NSApplicationIcon"]];
235           
236            [fNameField setStringValue: NSLocalizedString(@"No Torrents Selected", "Inspector -> above tabs -> selected torrents")];
237            [fBasicInfoField setStringValue: @""];
238            [fBasicInfoField setToolTip: @""];
239   
240            [fHaveField setStringValue: @""];
241            [fDownloadedTotalField setStringValue: @""];
242            [fUploadedTotalField setStringValue: @""];
243            [fFailedHashField setStringValue: @""];
244            [fDateActivityField setStringValue: @""];
245           
246            //options fields
247            [fUploadLimitPopUp setEnabled: NO];
248            [fUploadLimitPopUp selectItemAtIndex: -1];
249            [fUploadLimitField setHidden: YES];
250            [fUploadLimitLabel setHidden: YES];
251            [fUploadLimitField setStringValue: @""];
252           
253            [fDownloadLimitPopUp setEnabled: NO];
254            [fDownloadLimitPopUp selectItemAtIndex: -1];
255            [fDownloadLimitField setHidden: YES];
256            [fDownloadLimitLabel setHidden: YES];
257            [fDownloadLimitField setStringValue: @""];
258           
259            [fRatioPopUp setEnabled: NO];
260            [fRatioPopUp selectItemAtIndex: -1];
261            [fRatioLimitField setHidden: YES];
262            [fRatioLimitField setStringValue: @""];
263           
264            [fPeersConnectField setEnabled: NO];
265            [fPeersConnectField setStringValue: @""];
266        }
267       
268        [fFileController setTorrent: nil];
269       
270        [fNameField setToolTip: nil];
271
272        [fTrackerField setStringValue: @""];
273        [fPiecesField setStringValue: @""];
274        [fHashField setStringValue: @""];
275        [fHashField setToolTip: nil];
276        [fSecureField setStringValue: @""];
277        [fCommentView setString: @""];
278       
279        [fCreatorField setStringValue: @""];
280        [fDateCreatedField setStringValue: @""];
281        [fCommentView setSelectable: NO];
282       
283        [fTorrentLocationField setStringValue: @""];
284        [fTorrentLocationField setToolTip: nil];
285        [fDataLocationField setStringValue: @""];
286        [fDataLocationField setToolTip: nil];
287       
288        [fRevealDataButton setHidden: YES];
289        [fRevealTorrentButton setHidden: YES];
290       
291        //don't allow empty fields to be selected
292        [fTrackerField setSelectable: NO];
293        [fHashField setSelectable: NO];
294        [fCreatorField setSelectable: NO];
295        [fTorrentLocationField setSelectable: NO];
296        [fDataLocationField setSelectable: NO];
297       
298        [fStateField setStringValue: @""];
299        [fProgressField setStringValue: @""];
300        [fRatioField setStringValue: @""];
301       
302        [fSwarmSpeedField setStringValue: @""];
303        [fErrorMessageView setString: @""];
304        [fErrorMessageView setSelectable: NO];
305       
306        [fAnnounceAddressField setStringValue: @""];
307        [fAnnounceAddressField setToolTip: nil];
308        [fAnnounceAddressField setSelectable: NO];
309        [fAnnounceLastField setStringValue: @""];
310        [fAnnounceResponseField setStringValue: @""];
311        [fAnnounceResponseField setToolTip: nil];
312        [fAnnounceResponseField setSelectable: NO];
313        [fAnnounceNextField setStringValue: @""];
314       
315        [fScrapeAddressField setStringValue: @""];
316        [fScrapeAddressField setToolTip: nil];
317        [fScrapeAddressField setSelectable: NO];
318        [fScrapeLastField setStringValue: @""];
319        [fScrapeResponseField setStringValue: @""];
320        [fScrapeResponseField setToolTip: nil];
321        [fScrapeResponseField setSelectable: NO];
322        [fScrapeNextField setStringValue: @""];
323       
324        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
325        [fDownloadingFromField setStringValue: @""];
326        [fUploadingToField setStringValue: @""];
327        [fKnownField setStringValue: @""];
328        [fSeedersField setStringValue: @""];
329        [fLeechersField setStringValue: @""];
330        [fCompletedFromTrackerField setStringValue: @""];
331       
332        [fDateAddedField setStringValue: @""];
333        [fDateCompletedField setStringValue: @""];
334       
335        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE];
336        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS];
337        [fPiecesControl setEnabled: NO];
338        [fPiecesView setTorrent: nil];
339       
340        [fPeers release];
341        fPeers = nil;
342       
343        [fTrackers release];
344        fTrackers = nil;
345    }
346    else
347    {   
348        Torrent * torrent = [fTorrents objectAtIndex: 0];
349       
350        [fFileController setTorrent: torrent];
351       
352        NSImage * icon = [[torrent icon] copy];
353        [icon setFlipped: NO];
354        [fImageView setImage: icon];
355        [icon release];
356       
357        NSString * name = [torrent name];
358        [fNameField setStringValue: name];
359        [fNameField setToolTip: name];
360       
361        NSString * basicString = [NSString stringForFileSize: [torrent size]];
362        if ([torrent folder])
363        {
364            NSString * fileString;
365            int fileCount = [torrent fileCount];
366            if (fileCount == 1)
367                fileString = NSLocalizedString(@"1 File", "Inspector -> above tabs -> selected torrents");
368            else
369                fileString= [NSString stringWithFormat: NSLocalizedString(@"%d Files",
370                                "Inspector -> above tabs -> selected torrents"), fileCount];
371            basicString = [NSString stringWithFormat: @"%@, %@", fileString, basicString];
372        }
373        [fBasicInfoField setStringValue: basicString];
374        [fBasicInfoField setToolTip: [NSString stringWithFormat: NSLocalizedString(@"%u bytes",
375                                        "Inspector -> above tabs -> selected torrents"), [torrent size]]];
376       
377        NSString * hashString = [torrent hashString];
378        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
379                                        [NSString stringForFileSize: [torrent pieceSize]]]];
380        [fHashField setStringValue: hashString];
381        [fHashField setToolTip: hashString];
382        [fSecureField setStringValue: [torrent privateTorrent]
383                        ? NSLocalizedString(@"Private Torrent, PEX automatically disabled", "Inspector -> private torrent")
384                        : NSLocalizedString(@"Public Torrent", "Inspector -> private torrent")];
385       
386        NSString * commentString = [torrent comment];
387        [fCommentView setString: commentString];
388       
389        NSString * creatorString = [torrent creator];
390        [fCreatorField setStringValue: creatorString];
391        [fDateCreatedField setObjectValue: [torrent dateCreated]];
392       
393        BOOL publicTorrent = [torrent publicTorrent];
394        [fTorrentLocationField setStringValue: publicTorrent
395                    ? [[torrent publicTorrentLocation] stringByAbbreviatingWithTildeInPath]
396                    : NSLocalizedString(@"Transmission Support Folder", "Torrent -> location when deleting original")];
397        if (publicTorrent)
398            [fTorrentLocationField setToolTip: [NSString stringWithFormat: @"%@\n\n%@",
399                        [torrent publicTorrentLocation], [torrent torrentLocation]]];
400        else
401            [fTorrentLocationField setToolTip: [torrent torrentLocation]];
402       
403        [fDateAddedField setObjectValue: [torrent dateAdded]];
404       
405        [fRevealDataButton setHidden: NO];
406        [fRevealTorrentButton setHidden: ![torrent publicTorrent]];
407       
408        //allow these fields to be selected
409        [fTrackerField setSelectable: YES];
410        [fHashField setSelectable: YES];
411        [fCommentView setSelectable: ![commentString isEqualToString: @""]];
412        [fCreatorField setSelectable: ![creatorString isEqualToString: @""]];
413        [fTorrentLocationField setSelectable: YES];
414        [fDataLocationField setSelectable: YES];
415        [fAnnounceAddressField setSelectable: YES];
416        [fScrapeAddressField setSelectable: YES];
417       
418        //set pieces view
419        BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"];
420        [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE];
421        [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS];
422        [fPiecesControl setEnabled: YES];
423        [fPiecesView setTorrent: torrent];
424       
425        //get trackers for table
426        [fTrackers release];
427        fTrackers = [[torrent allTrackers] retain];
428    }
429   
430    //update stats and settings
431    [self updateInfoStats];
432    [self updateOptions];
433   
434    [fTrackerTable reloadData];
435    [fPeerTable reloadData];
436}
437
438- (void) updateInfoStats
439{
440    switch ([fTabMatrix selectedTag])
441    {
442        case TAB_INFO_TAG:
443            [self updateInfoGeneral];
444            break;
445        case TAB_ACTIVITY_TAG:
446            [self updateInfoActivity];
447            break;
448        case TAB_TRACKER_TAG:
449            [self updateInfoTracker];
450            break;
451        case TAB_PEERS_TAG:
452            [self updateInfoPeers];
453            break;
454        case TAB_FILES_TAG:
455            [self updateInfoFiles];
456            break;
457    }
458}
459
460- (void) updateOptions
461{
462    if ([fTorrents count] == 0)
463        return;
464   
465    //get bandwidth info
466    NSEnumerator * enumerator = [fTorrents objectEnumerator];
467    Torrent * torrent = [enumerator nextObject]; //first torrent
468   
469    int uploadSpeedMode = [torrent speedMode: YES],
470        uploadSpeedLimit = [torrent speedLimit: YES],
471        downloadSpeedMode = [torrent speedMode: NO],
472        downloadSpeedLimit = [torrent speedLimit: NO];
473   
474    while ((torrent = [enumerator nextObject])
475            && (uploadSpeedMode != INVALID || uploadSpeedLimit != INVALID
476                || downloadSpeedMode != INVALID || downloadSpeedLimit != INVALID))
477    {
478        if (uploadSpeedMode != INVALID && uploadSpeedMode != [torrent speedMode: YES])
479            uploadSpeedMode = INVALID;
480       
481        if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
482            uploadSpeedLimit = INVALID;
483       
484        if (downloadSpeedMode != INVALID && downloadSpeedMode != [torrent speedMode: NO])
485            downloadSpeedMode = INVALID;
486       
487        if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
488            downloadSpeedLimit = INVALID;
489    }
490   
491    //set upload view
492    int index;
493    if (uploadSpeedMode == TR_SPEEDLIMIT_SINGLE)
494        index = OPTION_POPUP_LIMIT;
495    else if (uploadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
496        index = OPTION_POPUP_NO_LIMIT;
497    else if (uploadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
498        index = OPTION_POPUP_GLOBAL;
499    else
500        index = -1;
501    [fUploadLimitPopUp selectItemAtIndex: index];
502    [fUploadLimitPopUp setEnabled: YES];
503   
504    [fUploadLimitLabel setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
505    [fUploadLimitField setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
506    if (uploadSpeedLimit != INVALID)
507        [fUploadLimitField setIntValue: uploadSpeedLimit];
508    else
509        [fUploadLimitField setStringValue: @""];
510   
511    //set download view
512    if (downloadSpeedMode == TR_SPEEDLIMIT_SINGLE)
513        index = OPTION_POPUP_LIMIT;
514    else if (downloadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
515        index = OPTION_POPUP_NO_LIMIT;
516    else if (downloadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
517        index = OPTION_POPUP_GLOBAL;
518    else
519        index = -1;
520    [fDownloadLimitPopUp selectItemAtIndex: index];
521    [fDownloadLimitPopUp setEnabled: YES];
522   
523    [fDownloadLimitLabel setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
524    [fDownloadLimitField setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
525    if (downloadSpeedLimit != INVALID)
526        [fDownloadLimitField setIntValue: downloadSpeedLimit];
527    else
528        [fDownloadLimitField setStringValue: @""];
529   
530    //get ratio info
531    enumerator = [fTorrents objectEnumerator];
532    torrent = [enumerator nextObject]; //first torrent
533   
534    int checkRatio = [torrent ratioSetting];
535    float ratioLimit = [torrent ratioLimit];
536   
537    while ((torrent = [enumerator nextObject]) && (checkRatio != INVALID || checkRatio != INVALID))
538    {
539        if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
540            checkRatio = INVALID;
541       
542        if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
543            ratioLimit = INVALID;
544    }
545   
546    //set ratio view
547    if (checkRatio == NSOnState)
548        index = OPTION_POPUP_LIMIT;
549    else if (checkRatio == NSOffState)
550        index = OPTION_POPUP_NO_LIMIT;
551    else if (checkRatio == NSMixedState)
552        index = OPTION_POPUP_GLOBAL;
553    else
554        index = -1;
555    [fRatioPopUp selectItemAtIndex: index];
556    [fRatioPopUp setEnabled: YES];
557   
558    [fRatioLimitField setHidden: checkRatio != NSOnState];
559    if (ratioLimit != INVALID)
560        [fRatioLimitField setFloatValue: ratioLimit];
561    else
562        [fRatioLimitField setStringValue: @""];
563   
564    //get peer info
565    enumerator = [fTorrents objectEnumerator];
566    torrent = [enumerator nextObject]; //first torrent
567   
568    int maxPeers = [torrent maxPeerConnect];
569   
570    while ((torrent = [enumerator nextObject]))
571    {
572        if (maxPeers != [torrent maxPeerConnect])
573        {
574            maxPeers = INVALID;
575            break;
576        }
577    }
578   
579    //set peer view
580    [fPeersConnectField setEnabled: YES];
581    if (maxPeers != INVALID)
582        [fPeersConnectField setIntValue: maxPeers];
583    else
584        [fPeersConnectField setStringValue: @""];
585}
586
587- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
588{
589    NSRect windowRect = [window frame];
590    windowRect.size.width = [window minSize].width;
591    return windowRect;
592}
593
594- (void) setTab: (id) sender
595{
596    int oldTabTag = fCurrentTabTag;
597    fCurrentTabTag = [fTabMatrix selectedTag];
598    if (fCurrentTabTag == oldTabTag)
599        return;
600   
601    [self updateInfoStats];
602   
603    //take care of old view
604    float oldHeight = 0;
605    NSString * oldResizeSaveKey = nil;
606    if (oldTabTag != INVALID)
607    {
608        //deselect old tab item
609        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
610       
611        switch (oldTabTag)
612        {
613            case TAB_TRACKER_TAG:
614                oldResizeSaveKey = @"InspectorContentHeightTracker";
615                break;
616            case TAB_PEERS_TAG:
617                [fPeers release];
618                fPeers = nil;
619               
620                oldResizeSaveKey = @"InspectorContentHeightPeers";
621                break;
622            case TAB_FILES_TAG:
623                oldResizeSaveKey = @"InspectorContentHeightFiles";
624                break;
625        }
626       
627        NSView * oldView = [self tabViewForTag: oldTabTag];
628        oldHeight = [oldView frame].size.height;
629        if (oldResizeSaveKey)
630            [[NSUserDefaults standardUserDefaults] setFloat: oldHeight forKey: oldResizeSaveKey];
631       
632        //remove old view
633        [oldView setHidden: YES];
634        [oldView removeFromSuperview];
635    }
636   
637    //set new tab item
638    NSView * view = [self tabViewForTag: fCurrentTabTag];
639   
640    NSString * resizeSaveKey = nil;
641    NSString * identifier, * title;
642    switch (fCurrentTabTag)
643    {
644        case TAB_INFO_TAG:
645            identifier = TAB_INFO_IDENT;
646            title = NSLocalizedString(@"General Info", "Inspector -> title");
647            break;
648        case TAB_ACTIVITY_TAG:
649            identifier = TAB_ACTIVITY_IDENT;
650            title = NSLocalizedString(@"Activity", "Inspector -> title");
651           
652            [fPiecesView updateView: YES];
653            break;
654        case TAB_TRACKER_TAG:
655            identifier = TAB_TRACKER_IDENT;
656            title = NSLocalizedString(@"Tracker", "Inspector -> title");
657            resizeSaveKey = @"InspectorContentHeightTracker";
658            break;
659        case TAB_PEERS_TAG:
660            identifier = TAB_PEERS_IDENT;
661            title = NSLocalizedString(@"Peers", "Inspector -> title");
662            resizeSaveKey = @"InspectorContentHeightPeers";
663            break;
664        case TAB_FILES_TAG:
665            identifier = TAB_FILES_IDENT;
666            title = NSLocalizedString(@"Files", "Inspector -> title");
667            resizeSaveKey = @"InspectorContentHeightFiles";
668            break;
669        case TAB_OPTIONS_TAG:
670            identifier = TAB_OPTIONS_IDENT;
671            title = NSLocalizedString(@"Options", "Inspector -> title");
672            break;
673        default:
674            return;
675    }
676   
677    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
678   
679    NSWindow * window = [self window];
680   
681    [window setTitle: [NSString stringWithFormat: @"%@ - %@", title, NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]];
682   
683    //selected tab item
684    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
685   
686    NSRect windowRect = [window frame], viewRect = [view frame];
687   
688    if (resizeSaveKey)
689    {
690        float height = [[NSUserDefaults standardUserDefaults] floatForKey: resizeSaveKey];
691        if (height != 0)
692            viewRect.size.height = MAX(height, TAB_MIN_HEIGHT);
693    }
694   
695    float difference = (viewRect.size.height - oldHeight) * [window userSpaceScaleFactor];
696    windowRect.origin.y -= difference;
697    windowRect.size.height += difference;
698   
699    if (resizeSaveKey)
700    {
701        if (!oldResizeSaveKey)
702        {
703            [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height - viewRect.size.height + TAB_MIN_HEIGHT)];
704            [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
705        }
706    }
707    else
708    {
709        [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height)];
710        [window setMaxSize: NSMakeSize(FLT_MAX, windowRect.size.height)];
711    }
712   
713    viewRect.size.width = windowRect.size.width;
714    [view setFrame: viewRect];
715   
716    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
717    [[window contentView] addSubview: view];
718    [view setHidden: NO];
719}
720
721- (void) setNextTab
722{
723    int tag = [fTabMatrix selectedTag]+1;
724    if (tag >= [fTabMatrix numberOfColumns])
725        tag = 0;
726   
727    [fTabMatrix selectCellWithTag: tag];
728    [self setTab: nil];
729}
730
731- (void) setPreviousTab
732{
733    int tag = [fTabMatrix selectedTag]-1;
734    if (tag < 0)
735        tag = [fTabMatrix numberOfColumns]-1;
736   
737    [fTabMatrix selectCellWithTag: tag];
738    [self setTab: nil];
739}
740
741- (int) numberOfRowsInTableView: (NSTableView *) tableView
742{
743    if (tableView == fPeerTable)
744        return fPeers ? [fPeers count] : 0;
745    else if (tableView == fTrackerTable)
746        return fTrackers ? [fTrackers count] : 0;
747    return 0;
748}
749
750- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
751{
752    if (tableView == fPeerTable)
753    {
754        NSString * ident = [column identifier];
755        NSDictionary * peer = [fPeers objectAtIndex: row];
756       
757        if ([ident isEqualToString: @"Encryption"])
758            return [[peer objectForKey: @"Encryption"] boolValue] ? [NSImage imageNamed: @"Lock.png"] : nil;
759        else if ([ident isEqualToString: @"Client"])
760            return [peer objectForKey: @"Client"];
761        else if  ([ident isEqualToString: @"Progress"])
762            return [peer objectForKey: @"Progress"];
763        else if ([ident isEqualToString: @"UL To"])
764        {
765            NSNumber * rate;
766            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
767        }
768        else if ([ident isEqualToString: @"DL From"])
769        {
770            NSNumber * rate;
771            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
772        }
773        else
774            return [peer objectForKey: @"IP"];
775    }
776    else if (tableView == fTrackerTable)
777    {
778        id item = [fTrackers objectAtIndex: row];
779        if ([item isKindOfClass: [NSNumber class]])
780            return [NSString stringWithFormat: NSLocalizedString(@"Tier %d", "Inspector -> tracker table"), [item intValue]+1];
781        else
782            return item;
783    }
784    return nil;
785}
786
787- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
788{
789    if (tableView == fPeerTable)
790    {
791        if (fPeers)
792        {
793            NSArray * oldPeers = fPeers;
794            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
795            [oldPeers release];
796            [tableView reloadData];
797        }
798    }
799}
800
801- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) row
802{
803    return NO;
804}
805
806- (BOOL) tableView: (NSTableView *) tableView isGroupRow: (NSInteger) row
807{
808    if (tableView == fTrackerTable)
809        return [[fTrackers objectAtIndex: row] isKindOfClass: [NSNumber class]];
810    return NO;
811}
812
813- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
814                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
815{
816    if (tableView == fPeerTable)
817    {
818        NSDictionary * peer = [fPeers objectAtIndex: row];
819        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 5];
820       
821        [components addObject: [NSString stringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
822            "Inspector -> Peers tab -> table row tooltip"), [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
823       
824        if ([[peer objectForKey: @"Encryption"] boolValue])
825            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
826       
827        int port;
828        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
829            [components addObject: [NSString stringWithFormat:
830                                    NSLocalizedString(@"Port: %d", "Inspector -> Peers tab -> table row tooltip"), port]];
831        else
832            [components addObject: NSLocalizedString(@"Port: N/A", "Inspector -> Peers tab -> table row tooltip")];
833       
834        switch ([[peer objectForKey: @"From"] intValue])
835        {
836            case TR_PEER_FROM_TRACKER:
837                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
838                break;
839            case TR_PEER_FROM_INCOMING:
840                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
841                break;
842            case TR_PEER_FROM_CACHE:
843                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
844                break;
845            case TR_PEER_FROM_PEX:
846                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
847                break;
848        }
849       
850        //determing status strings from flags
851        NSMutableArray * statusArray = [NSMutableArray arrayWithCapacity: 3];
852        NSString * flags = [peer objectForKey: @"Flags"];
853       
854        if ([flags rangeOfString: @"D"].location != NSNotFound)
855            [statusArray addObject: NSLocalizedString(@"Currently downloading (interested and not choked)",
856                "Inspector -> peer -> status")];
857        else if ([flags rangeOfString: @"d"].location != NSNotFound)
858            [statusArray addObject: NSLocalizedString(@"You want to download, but peer does not want to send (interested and choked)",
859                "Inspector -> peer -> status")];
860        else;
861       
862        if ([flags rangeOfString: @"U"].location != NSNotFound)
863            [statusArray addObject: NSLocalizedString(@"Currently uploading (interested and not choked)",
864                "Inspector -> peer -> status")];
865        else if ([flags rangeOfString: @"u"].location != NSNotFound)
866            [statusArray addObject: NSLocalizedString(@"Peer wants you to upload, but you do not want to (interested and choked)",
867                "Inspector -> peer -> status")];
868        else;
869       
870        if ([flags rangeOfString: @"K"].location != NSNotFound)
871            [statusArray addObject: NSLocalizedString(@"Peer is unchoking you, but you are not interested",
872                "Inspector -> peer -> status")];
873       
874        if ([flags rangeOfString: @"?"].location != NSNotFound)
875            [statusArray addObject: NSLocalizedString(@"You unchoked the peer, but the peer is not interested",
876                "Inspector -> peer -> status")];
877       
878        if ([statusArray count] > 0)
879            [components addObject: [@"\n" stringByAppendingString: [statusArray componentsJoinedByString: @"\n\n"]]];
880       
881        return [components componentsJoinedByString: @"\n"];
882    }
883    return nil;
884}
885
886- (void) setPiecesView: (id) sender
887{
888    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
889}
890
891- (void) setPiecesViewForAvailable: (BOOL) available
892{
893    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
894    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
895   
896    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
897    [fPiecesView updateView: YES];
898}
899
900- (void) revealTorrentFile: (id) sender
901{
902    if ([fTorrents count] > 0)
903        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
904}
905
906- (void) revealDataFile: (id) sender
907{
908    if ([fTorrents count] > 0)
909        [[fTorrents objectAtIndex: 0] revealData];
910}
911
912- (void) setSpeedMode: (id) sender
913{
914    BOOL upload = sender == fUploadLimitPopUp;
915    int mode;
916    switch ([sender indexOfSelectedItem])
917    {
918        case OPTION_POPUP_LIMIT:
919            mode = TR_SPEEDLIMIT_SINGLE;
920            break;
921        case OPTION_POPUP_NO_LIMIT:
922            mode = TR_SPEEDLIMIT_UNLIMITED;
923            break;
924        case OPTION_POPUP_GLOBAL:
925            mode = TR_SPEEDLIMIT_GLOBAL;
926            break;
927        default:
928            return;
929    }
930   
931    Torrent * torrent;
932    NSEnumerator * enumerator = [fTorrents objectEnumerator];
933    while ((torrent = [enumerator nextObject]))
934        [torrent setSpeedMode: mode upload: upload];
935   
936    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
937   
938    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
939    [field setHidden: !single];
940    if (single)
941    {
942        [field selectText: self];
943        [[self window] makeKeyAndOrderFront:self];
944    }
945   
946    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
947    [label setHidden: !single];
948}
949
950- (void) setSpeedLimit: (id) sender
951{
952    BOOL upload = sender == fUploadLimitField;
953    int limit = [sender intValue];
954   
955    Torrent * torrent;
956    NSEnumerator * enumerator = [fTorrents objectEnumerator];
957   
958    while ((torrent = [enumerator nextObject]))
959        [torrent setSpeedLimit: limit upload: upload];
960}
961
962- (void) setRatioSetting: (id) sender
963{
964    int setting;
965    switch ([sender indexOfSelectedItem])
966    {
967        case OPTION_POPUP_LIMIT:
968            setting = NSOnState;
969            break;
970        case OPTION_POPUP_NO_LIMIT:
971            setting = NSOffState;
972            break;
973        case OPTION_POPUP_GLOBAL:
974            setting = NSMixedState;
975            break;
976        default:
977            return;
978    }
979   
980    Torrent * torrent;
981    NSEnumerator * enumerator = [fTorrents objectEnumerator];
982    while ((torrent = [enumerator nextObject]))
983        [torrent setRatioSetting: setting];
984   
985    BOOL single = setting == NSOnState;
986    [fRatioLimitField setHidden: !single];
987    if (single)
988    {
989        [fRatioLimitField selectText: self];
990        [[self window] makeKeyAndOrderFront:self];
991    }
992   
993    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
994}
995
996- (void) setRatioLimit: (id) sender
997{
998    float limit = [sender floatValue];
999   
1000    Torrent * torrent;
1001    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1002    while ((torrent = [enumerator nextObject]))
1003        [torrent setRatioLimit: limit];
1004}
1005
1006- (void) setPeersConnectLimit: (id) sender
1007{
1008    int limit = [sender intValue];
1009   
1010    Torrent * torrent;
1011    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1012    while ((torrent = [enumerator nextObject]))
1013        [torrent setMaxPeerConnect: limit];
1014}
1015
1016
1017- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
1018{
1019    [fInitialString release];
1020    fInitialString = [[control stringValue] retain];
1021   
1022    return YES;
1023}
1024
1025- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
1026{
1027    NSBeep();
1028    if (fInitialString)
1029    {
1030        [control setStringValue: fInitialString];
1031        [fInitialString release];
1032        fInitialString = nil;
1033    }
1034    return NO;
1035}
1036
1037@end
1038
1039@implementation InfoWindowController (Private)
1040
1041- (void) updateInfoGeneral
1042{   
1043    if ([fTorrents count] != 1)
1044        return;
1045   
1046    Torrent * torrent = [fTorrents objectAtIndex: 0];
1047   
1048    [fTrackerField setStringValue: [torrent trackerAddress]];
1049   
1050    NSString * location = [torrent dataLocation];
1051    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1052    [fDataLocationField setToolTip: location];
1053}
1054
1055- (void) updateInfoActivity
1056{
1057    int numberSelected = [fTorrents count];
1058    if (numberSelected == 0)
1059        return;
1060   
1061    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1062    NSDate * lastActivity = nil;
1063    Torrent * torrent;
1064    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1065    while ((torrent = [enumerator nextObject]))
1066    {
1067        have += [torrent haveTotal];
1068        haveVerified += [torrent haveVerified];
1069        downloadedTotal += [torrent downloadedTotal];
1070        uploadedTotal += [torrent uploadedTotal];
1071        failedHash += [torrent failedHash];
1072       
1073        NSDate * nextLastActivity;
1074        if ((nextLastActivity = [torrent dateActivity]))
1075            lastActivity = lastActivity ? [lastActivity laterDate: nextLastActivity] : nextLastActivity;
1076    }
1077   
1078    if (have == 0)
1079        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1080    else if (have == haveVerified)
1081        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ verified",
1082                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: haveVerified]]];
1083    else
1084        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ (%@ verified)",
1085                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: have], [NSString stringForFileSize: haveVerified]]];
1086   
1087    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1088    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1089    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1090   
1091    [fDateActivityField setObjectValue: lastActivity];
1092   
1093    if (numberSelected == 1)
1094    {
1095        torrent = [fTorrents objectAtIndex: 0];
1096       
1097        [fStateField setStringValue: [torrent stateString]];
1098        [fProgressField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1099                    "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1100        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1101        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1102       
1103        NSString * errorMessage = [torrent errorMessage];
1104        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1105        {
1106            [fErrorMessageView setString: errorMessage];
1107            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1108        }
1109       
1110        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1111       
1112        [fPiecesView updateView: NO];
1113    }
1114}
1115
1116- (void) updateInfoTracker
1117{
1118    if ([fTorrents count] != 1)
1119        return;
1120    Torrent * torrent = [fTorrents objectAtIndex: 0];
1121   
1122    //announce fields
1123    NSString * announceAddress = [[torrent trackerAddress] stringByAppendingString: [torrent trackerAddressAnnounce]];
1124    [fAnnounceAddressField setStringValue: announceAddress];
1125    [fAnnounceAddressField setToolTip: announceAddress];
1126   
1127    [fAnnounceLastField setObjectValue: [torrent lastAnnounceTime]];
1128   
1129    NSString * announceResponse = [torrent announceResponse];
1130    [fAnnounceResponseField setStringValue: announceResponse];
1131    [fAnnounceResponseField setToolTip: announceResponse];
1132    [fAnnounceResponseField setSelectable: ![announceResponse isEqualToString: @""]];
1133   
1134    int announceNext = [torrent nextAnnounceTime];
1135    [fAnnounceNextField setStringValue: announceNext > 0 ? [NSString timeString: announceNext showSeconds: YES] : @""];
1136   
1137    //scrape fields
1138    NSString * scrapeAddress = [[torrent trackerAddress] stringByAppendingString: [torrent trackerAddressScrape]];
1139    [fScrapeAddressField setStringValue: scrapeAddress];
1140    [fScrapeAddressField setToolTip: scrapeAddress];
1141   
1142    [fScrapeLastField setObjectValue: [torrent lastScrapeTime]];
1143   
1144    NSString * scrapeResponse = [torrent scrapeResponse];
1145    [fScrapeResponseField setStringValue: scrapeResponse];
1146    [fScrapeResponseField setToolTip: scrapeResponse];
1147    [fScrapeResponseField setSelectable: ![scrapeResponse isEqualToString: @""]];
1148   
1149    int scrapeNext = [torrent nextScrapeTime];
1150    [fScrapeNextField setStringValue: scrapeNext > 0 ? [NSString timeString: scrapeNext showSeconds: YES] : @""];
1151}
1152
1153- (void) updateInfoPeers
1154{
1155    if ([fTorrents count] != 1)
1156        return;
1157    Torrent * torrent = [fTorrents objectAtIndex: 0];
1158   
1159    int seeders = [torrent seeders], leechers = [torrent leechers], completed = [torrent completedFromTracker];
1160    [fSeedersField setStringValue: seeders >= 0 ? [NSString stringWithFormat: @"%d", seeders] : @""];
1161    [fLeechersField setStringValue: leechers >= 0 ? [NSString stringWithFormat: @"%d", leechers] : @""];
1162    [fCompletedFromTrackerField setStringValue: completed >= 0 ? [NSString stringWithFormat: @"%d", completed] : @""];
1163   
1164    BOOL active = [torrent isActive];
1165   
1166    if (active)
1167    {
1168        int total = [torrent totalPeersConnected];
1169        NSString * connected = [NSString stringWithFormat:
1170                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1171       
1172        if (total > 0)
1173        {
1174            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
1175            int count;
1176            if ((count = [torrent totalPeersTracker]) > 0)
1177                [components addObject: [NSString stringWithFormat:
1178                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1179            if ((count = [torrent totalPeersIncoming]) > 0)
1180                [components addObject: [NSString stringWithFormat:
1181                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1182            if ((count = [torrent totalPeersPex]) > 0)
1183                [components addObject: [NSString stringWithFormat:
1184                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1185            if ((count = [torrent totalPeersCache]) > 0)
1186                [components addObject: [NSString stringWithFormat:
1187                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1188           
1189            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1190        }
1191       
1192        [fConnectedPeersField setStringValue: connected];
1193       
1194        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1195        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1196    }
1197    else
1198    {
1199        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
1200        [fDownloadingFromField setStringValue: @""];
1201        [fUploadingToField setStringValue: @""];
1202    }
1203   
1204    [fKnownField setIntValue: [torrent totalPeersKnown]];
1205   
1206    [fPeers release];
1207    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1208   
1209    [fPeerTable reloadData];
1210}
1211
1212- (void) updateInfoFiles
1213{
1214    if ([fTorrents count] == 1)
1215        [fFileController reloadData];
1216}
1217
1218- (NSView *) tabViewForTag: (int) tag
1219{
1220    switch (tag)
1221    {
1222        case TAB_INFO_TAG:
1223            return fInfoView;
1224        case TAB_ACTIVITY_TAG:
1225            return fActivityView;
1226        case TAB_TRACKER_TAG:
1227            return fTrackerView;
1228        case TAB_PEERS_TAG:
1229            return fPeersView;
1230        case TAB_FILES_TAG:
1231            return fFilesView;
1232        case TAB_OPTIONS_TAG:
1233            return fOptionsView;
1234        default:
1235            return nil;
1236    }
1237}
1238
1239- (NSArray *) peerSortDescriptors
1240{
1241    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1242   
1243    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1244    BOOL useSecond = YES, asc = YES;
1245    if ([oldDescriptors count] > 0)
1246    {
1247        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1248        [descriptors addObject: descriptor];
1249       
1250        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1251            asc = [descriptor ascending];
1252    }
1253   
1254    //sort by IP after primary sort
1255    if (useSecond)
1256    {
1257        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1258                                                                        selector: @selector(compareIP:)];
1259        [descriptors addObject: secondDescriptor];
1260        [secondDescriptor release];
1261    }
1262   
1263    return descriptors;
1264}
1265
1266@end
Note: See TracBrowser for help on using the repository browser.