source: trunk/macosx/InfoWindowController.m @ 5717

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

eliminate some more localized strings

  • Property svn:keywords set to Date Rev Author Id
File size: 46.7 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 5717 2008-04-28 18:44:00Z 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 -> 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", "Inspector -> selected torrents"),
230                                            size]];
231        }
232        else
233        {
234            [fImageView setImage: [NSImage imageNamed: @"NSApplicationIcon"]];
235           
236            [fNameField setStringValue: NSLocalizedString(@"No Torrents Selected", "Inspector -> 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: @""];
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 -> selected torrents");
368            else
369                fileString= [NSString stringWithFormat: NSLocalizedString(@"%d files", "Inspector -> selected torrents"), fileCount];
370            basicString = [NSString stringWithFormat: @"%@, %@", fileString, basicString];
371        }
372        [fBasicInfoField setStringValue: basicString];
373        [fBasicInfoField setToolTip: [NSString stringWithFormat: NSLocalizedString(@"%u bytes", "Inspector -> selected torrents"),
374                                        [torrent size]]];
375       
376        NSString * hashString = [torrent hashString];
377        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
378                                        [NSString stringForFileSize: [torrent pieceSize]]]];
379        [fHashField setStringValue: hashString];
380        [fHashField setToolTip: hashString];
381        [fSecureField setStringValue: [torrent privateTorrent]
382                        ? NSLocalizedString(@"Private Torrent, PEX automatically disabled", "Inspector -> private torrent")
383                        : NSLocalizedString(@"Public Torrent", "Inspector -> private torrent")];
384       
385        NSString * commentString = [torrent comment];
386        [fCommentView setString: commentString];
387       
388        NSString * creatorString = [torrent creator];
389        [fCreatorField setStringValue: creatorString];
390        [fDateCreatedField setObjectValue: [torrent dateCreated]];
391       
392        BOOL publicTorrent = [torrent publicTorrent];
393        [fTorrentLocationField setStringValue: publicTorrent
394                    ? [[torrent publicTorrentLocation] stringByAbbreviatingWithTildeInPath]
395                    : NSLocalizedString(@"Transmission Support Folder", "Torrent -> location when deleting original")];
396        if (publicTorrent)
397            [fTorrentLocationField setToolTip: [NSString stringWithFormat: @"%@\n\n%@",
398                        [torrent publicTorrentLocation], [torrent torrentLocation]]];
399        else
400            [fTorrentLocationField setToolTip: [torrent torrentLocation]];
401       
402        [fDateAddedField setObjectValue: [torrent dateAdded]];
403       
404        [fRevealDataButton setHidden: NO];
405        [fRevealTorrentButton setHidden: ![torrent publicTorrent]];
406       
407        //allow these fields to be selected
408        [fTrackerField setSelectable: YES];
409        [fHashField setSelectable: YES];
410        [fCommentView setSelectable: ![commentString isEqualToString: @""]];
411        [fCreatorField setSelectable: ![creatorString isEqualToString: @""]];
412        [fTorrentLocationField setSelectable: YES];
413        [fDataLocationField setSelectable: YES];
414        [fAnnounceAddressField setSelectable: YES];
415        [fScrapeAddressField setSelectable: YES];
416       
417        //set pieces view
418        BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"];
419        [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE];
420        [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS];
421        [fPiecesControl setEnabled: YES];
422        [fPiecesView setTorrent: torrent];
423       
424        //get trackers for table
425        [fTrackers release];
426        fTrackers = [[torrent allTrackers: YES] retain];
427    }
428   
429    //update stats and settings
430    [self updateInfoStats];
431    [self updateOptions];
432   
433    [fTrackerTable reloadData];
434    [fPeerTable reloadData];
435}
436
437- (void) updateInfoStats
438{
439    switch ([fTabMatrix selectedTag])
440    {
441        case TAB_INFO_TAG:
442            [self updateInfoGeneral];
443            break;
444        case TAB_ACTIVITY_TAG:
445            [self updateInfoActivity];
446            break;
447        case TAB_TRACKER_TAG:
448            [self updateInfoTracker];
449            break;
450        case TAB_PEERS_TAG:
451            [self updateInfoPeers];
452            break;
453        case TAB_FILES_TAG:
454            [self updateInfoFiles];
455            break;
456    }
457}
458
459- (void) updateOptions
460{
461    if ([fTorrents count] == 0)
462        return;
463   
464    //get bandwidth info
465    NSEnumerator * enumerator = [fTorrents objectEnumerator];
466    Torrent * torrent = [enumerator nextObject]; //first torrent
467   
468    int uploadSpeedMode = [torrent speedMode: YES],
469        uploadSpeedLimit = [torrent speedLimit: YES],
470        downloadSpeedMode = [torrent speedMode: NO],
471        downloadSpeedLimit = [torrent speedLimit: NO];
472   
473    while ((torrent = [enumerator nextObject])
474            && (uploadSpeedMode != INVALID || uploadSpeedLimit != INVALID
475                || downloadSpeedMode != INVALID || downloadSpeedLimit != INVALID))
476    {
477        if (uploadSpeedMode != INVALID && uploadSpeedMode != [torrent speedMode: YES])
478            uploadSpeedMode = INVALID;
479       
480        if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
481            uploadSpeedLimit = INVALID;
482       
483        if (downloadSpeedMode != INVALID && downloadSpeedMode != [torrent speedMode: NO])
484            downloadSpeedMode = INVALID;
485       
486        if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
487            downloadSpeedLimit = INVALID;
488    }
489   
490    //set upload view
491    int index;
492    if (uploadSpeedMode == TR_SPEEDLIMIT_SINGLE)
493        index = OPTION_POPUP_LIMIT;
494    else if (uploadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
495        index = OPTION_POPUP_NO_LIMIT;
496    else if (uploadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
497        index = OPTION_POPUP_GLOBAL;
498    else
499        index = -1;
500    [fUploadLimitPopUp selectItemAtIndex: index];
501    [fUploadLimitPopUp setEnabled: YES];
502   
503    [fUploadLimitLabel setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
504    [fUploadLimitField setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
505    if (uploadSpeedLimit != INVALID)
506        [fUploadLimitField setIntValue: uploadSpeedLimit];
507    else
508        [fUploadLimitField setStringValue: @""];
509   
510    //set download view
511    if (downloadSpeedMode == TR_SPEEDLIMIT_SINGLE)
512        index = OPTION_POPUP_LIMIT;
513    else if (downloadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
514        index = OPTION_POPUP_NO_LIMIT;
515    else if (downloadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
516        index = OPTION_POPUP_GLOBAL;
517    else
518        index = -1;
519    [fDownloadLimitPopUp selectItemAtIndex: index];
520    [fDownloadLimitPopUp setEnabled: YES];
521   
522    [fDownloadLimitLabel setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
523    [fDownloadLimitField setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
524    if (downloadSpeedLimit != INVALID)
525        [fDownloadLimitField setIntValue: downloadSpeedLimit];
526    else
527        [fDownloadLimitField setStringValue: @""];
528   
529    //get ratio info
530    enumerator = [fTorrents objectEnumerator];
531    torrent = [enumerator nextObject]; //first torrent
532   
533    int checkRatio = [torrent ratioSetting];
534    float ratioLimit = [torrent ratioLimit];
535   
536    while ((torrent = [enumerator nextObject]) && (checkRatio != INVALID || checkRatio != INVALID))
537    {
538        if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
539            checkRatio = INVALID;
540       
541        if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
542            ratioLimit = INVALID;
543    }
544   
545    //set ratio view
546    if (checkRatio == NSOnState)
547        index = OPTION_POPUP_LIMIT;
548    else if (checkRatio == NSOffState)
549        index = OPTION_POPUP_NO_LIMIT;
550    else if (checkRatio == NSMixedState)
551        index = OPTION_POPUP_GLOBAL;
552    else
553        index = -1;
554    [fRatioPopUp selectItemAtIndex: index];
555    [fRatioPopUp setEnabled: YES];
556   
557    [fRatioLimitField setHidden: checkRatio != NSOnState];
558    if (ratioLimit != INVALID)
559        [fRatioLimitField setFloatValue: ratioLimit];
560    else
561        [fRatioLimitField setStringValue: @""];
562   
563    //get peer info
564    enumerator = [fTorrents objectEnumerator];
565    torrent = [enumerator nextObject]; //first torrent
566   
567    int maxPeers = [torrent maxPeerConnect];
568   
569    while ((torrent = [enumerator nextObject]))
570    {
571        if (maxPeers != [torrent maxPeerConnect])
572        {
573            maxPeers = INVALID;
574            break;
575        }
576    }
577   
578    //set peer view
579    [fPeersConnectField setEnabled: YES];
580    if (maxPeers != INVALID)
581        [fPeersConnectField setIntValue: maxPeers];
582    else
583        [fPeersConnectField setStringValue: @""];
584}
585
586- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
587{
588    NSRect windowRect = [window frame];
589    windowRect.size.width = [window minSize].width;
590    return windowRect;
591}
592
593- (void) setTab: (id) sender
594{
595    int oldTabTag = fCurrentTabTag;
596    fCurrentTabTag = [fTabMatrix selectedTag];
597    if (fCurrentTabTag == oldTabTag)
598        return;
599   
600    [self updateInfoStats];
601   
602    //take care of old view
603    float oldHeight = 0;
604    NSString * oldResizeSaveKey = nil;
605    if (oldTabTag != INVALID)
606    {
607        //deselect old tab item
608        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
609       
610        switch (oldTabTag)
611        {
612            case TAB_TRACKER_TAG:
613                oldResizeSaveKey = @"InspectorContentHeightTracker";
614                break;
615            case TAB_PEERS_TAG:
616                [fPeers release];
617                fPeers = nil;
618               
619                oldResizeSaveKey = @"InspectorContentHeightPeers";
620                break;
621            case TAB_FILES_TAG:
622                oldResizeSaveKey = @"InspectorContentHeightFiles";
623                break;
624        }
625       
626        NSView * oldView = [self tabViewForTag: oldTabTag];
627        oldHeight = [oldView frame].size.height;
628        if (oldResizeSaveKey)
629            [[NSUserDefaults standardUserDefaults] setFloat: oldHeight forKey: oldResizeSaveKey];
630       
631        //remove old view
632        [oldView setHidden: YES];
633        [oldView removeFromSuperview];
634    }
635   
636    //set new tab item
637    NSView * view = [self tabViewForTag: fCurrentTabTag];
638   
639    NSString * resizeSaveKey = nil;
640    NSString * identifier, * title;
641    switch (fCurrentTabTag)
642    {
643        case TAB_INFO_TAG:
644            identifier = TAB_INFO_IDENT;
645            title = NSLocalizedString(@"General Info", "Inspector -> title");
646            break;
647        case TAB_ACTIVITY_TAG:
648            identifier = TAB_ACTIVITY_IDENT;
649            title = NSLocalizedString(@"Activity", "Inspector -> title");
650           
651            [fPiecesView updateView: YES];
652            break;
653        case TAB_TRACKER_TAG:
654            identifier = TAB_TRACKER_IDENT;
655            title = NSLocalizedString(@"Tracker", "Inspector -> title");
656            resizeSaveKey = @"InspectorContentHeightTracker";
657            break;
658        case TAB_PEERS_TAG:
659            identifier = TAB_PEERS_IDENT;
660            title = NSLocalizedString(@"Peers", "Inspector -> title");
661            resizeSaveKey = @"InspectorContentHeightPeers";
662            break;
663        case TAB_FILES_TAG:
664            identifier = TAB_FILES_IDENT;
665            title = NSLocalizedString(@"Files", "Inspector -> title");
666            resizeSaveKey = @"InspectorContentHeightFiles";
667            break;
668        case TAB_OPTIONS_TAG:
669            identifier = TAB_OPTIONS_IDENT;
670            title = NSLocalizedString(@"Options", "Inspector -> title");
671            break;
672        default:
673            return;
674    }
675   
676    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
677   
678    NSWindow * window = [self window];
679   
680    [window setTitle: [NSString stringWithFormat: @"%@ - %@", title, NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]];
681   
682    //selected tab item
683    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
684   
685    NSRect windowRect = [window frame], viewRect = [view frame];
686   
687    if (resizeSaveKey)
688    {
689        float height = [[NSUserDefaults standardUserDefaults] floatForKey: resizeSaveKey];
690        if (height != 0)
691            viewRect.size.height = MAX(height, TAB_MIN_HEIGHT);
692    }
693   
694    float difference = (viewRect.size.height - oldHeight) * [window userSpaceScaleFactor];
695    windowRect.origin.y -= difference;
696    windowRect.size.height += difference;
697   
698    if (resizeSaveKey)
699    {
700        if (!oldResizeSaveKey)
701        {
702            [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height - viewRect.size.height + TAB_MIN_HEIGHT)];
703            [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
704        }
705    }
706    else
707    {
708        [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height)];
709        [window setMaxSize: NSMakeSize(FLT_MAX, windowRect.size.height)];
710    }
711   
712    viewRect.size.width = windowRect.size.width;
713    [view setFrame: viewRect];
714   
715    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
716    [[window contentView] addSubview: view];
717    [view setHidden: NO];
718}
719
720- (void) setNextTab
721{
722    int tag = [fTabMatrix selectedTag]+1;
723    if (tag >= [fTabMatrix numberOfColumns])
724        tag = 0;
725   
726    [fTabMatrix selectCellWithTag: tag];
727    [self setTab: nil];
728}
729
730- (void) setPreviousTab
731{
732    int tag = [fTabMatrix selectedTag]-1;
733    if (tag < 0)
734        tag = [fTabMatrix numberOfColumns]-1;
735   
736    [fTabMatrix selectCellWithTag: tag];
737    [self setTab: nil];
738}
739
740- (int) numberOfRowsInTableView: (NSTableView *) tableView
741{
742    if (tableView == fPeerTable)
743        return fPeers ? [fPeers count] : 0;
744    else if (tableView == fTrackerTable)
745        return fTrackers ? [fTrackers count] : 0;
746    return 0;
747}
748
749- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
750{
751    if (tableView == fPeerTable)
752    {
753        NSString * ident = [column identifier];
754        NSDictionary * peer = [fPeers objectAtIndex: row];
755       
756        if ([ident isEqualToString: @"Encryption"])
757            return [[peer objectForKey: @"Encryption"] boolValue] ? [NSImage imageNamed: @"Lock.png"] : nil;
758        else if ([ident isEqualToString: @"Client"])
759            return [peer objectForKey: @"Client"];
760        else if  ([ident isEqualToString: @"Progress"])
761            return [peer objectForKey: @"Progress"];
762        else if ([ident isEqualToString: @"UL To"])
763        {
764            NSNumber * rate;
765            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
766        }
767        else if ([ident isEqualToString: @"DL From"])
768        {
769            NSNumber * rate;
770            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
771        }
772        else
773            return [peer objectForKey: @"IP"];
774    }
775    else if (tableView == fTrackerTable)
776    {
777        id item = [fTrackers objectAtIndex: row];
778        if ([item isKindOfClass: [NSNumber class]])
779            return [NSString stringWithFormat: NSLocalizedString(@"Tier %d", "Inspector -> tracker table"), [item intValue]+1];
780        else
781            return item;
782    }
783    return nil;
784}
785
786- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
787{
788    if (tableView == fPeerTable)
789    {
790        if (fPeers)
791        {
792            NSArray * oldPeers = fPeers;
793            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
794            [oldPeers release];
795            [tableView reloadData];
796        }
797    }
798}
799
800- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) row
801{
802    return NO;
803}
804
805- (BOOL) tableView: (NSTableView *) tableView isGroupRow: (NSInteger) row
806{
807    if (tableView == fTrackerTable)
808        return [[fTrackers objectAtIndex: row] isKindOfClass: [NSNumber class]];
809    return NO;
810}
811
812- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
813                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
814{
815    if (tableView == fPeerTable)
816    {
817        NSDictionary * peer = [fPeers objectAtIndex: row];
818        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 5];
819       
820        [components addObject: [NSString localizedStringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
821            "Inspector -> Peers tab -> table row tooltip"), [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
822       
823        if ([[peer objectForKey: @"Encryption"] boolValue])
824            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
825       
826        NSString * portString;
827        int port;
828        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
829            portString = [NSString stringWithFormat: @"%d", port];
830        else
831            portString = NSLocalizedString(@"N/A", "Inspector -> Peers tab -> table row tooltip");
832        [components addObject: [NSString stringWithFormat: @"%@: %@", NSLocalizedString(@"Port",
833            "Inspector -> Peers tab -> table row tooltip"), portString]];
834       
835        switch ([[peer objectForKey: @"From"] intValue])
836        {
837            case TR_PEER_FROM_TRACKER:
838                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
839                break;
840            case TR_PEER_FROM_INCOMING:
841                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
842                break;
843            case TR_PEER_FROM_CACHE:
844                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
845                break;
846            case TR_PEER_FROM_PEX:
847                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
848                break;
849        }
850       
851        //determing status strings from flags
852        NSMutableArray * statusArray = [NSMutableArray arrayWithCapacity: 3];
853        NSString * flags = [peer objectForKey: @"Flags"];
854       
855        if ([flags rangeOfString: @"D"].location != NSNotFound)
856            [statusArray addObject: NSLocalizedString(@"Currently downloading (interested and not choked)",
857                "Inspector -> peer -> status")];
858        else if ([flags rangeOfString: @"d"].location != NSNotFound)
859            [statusArray addObject: NSLocalizedString(@"You want to download, but peer does not want to send (interested and choked)",
860                "Inspector -> peer -> status")];
861        else;
862       
863        if ([flags rangeOfString: @"U"].location != NSNotFound)
864            [statusArray addObject: NSLocalizedString(@"Currently uploading (interested and not choked)",
865                "Inspector -> peer -> status")];
866        else if ([flags rangeOfString: @"u"].location != NSNotFound)
867            [statusArray addObject: NSLocalizedString(@"Peer wants you to upload, but you do not want to (interested and choked)",
868                "Inspector -> peer -> status")];
869        else;
870       
871        if ([flags rangeOfString: @"K"].location != NSNotFound)
872            [statusArray addObject: NSLocalizedString(@"Peer is unchoking you, but you are not interested",
873                "Inspector -> peer -> status")];
874       
875        if ([flags rangeOfString: @"?"].location != NSNotFound)
876            [statusArray addObject: NSLocalizedString(@"You unchoked the peer, but the peer is not interested",
877                "Inspector -> peer -> status")];
878       
879        if ([statusArray count] > 0)
880            [components addObject: [@"\n" stringByAppendingString: [statusArray componentsJoinedByString: @"\n\n"]]];
881       
882        return [components componentsJoinedByString: @"\n"];
883    }
884    return nil;
885}
886
887- (void) setPiecesView: (id) sender
888{
889    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
890}
891
892- (void) setPiecesViewForAvailable: (BOOL) available
893{
894    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
895    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
896   
897    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
898    [fPiecesView updateView: YES];
899}
900
901- (void) revealTorrentFile: (id) sender
902{
903    if ([fTorrents count] > 0)
904        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
905}
906
907- (void) revealDataFile: (id) sender
908{
909    if ([fTorrents count] > 0)
910        [[fTorrents objectAtIndex: 0] revealData];
911}
912
913- (void) setSpeedMode: (id) sender
914{
915    BOOL upload = sender == fUploadLimitPopUp;
916    int mode;
917    switch ([sender indexOfSelectedItem])
918    {
919        case OPTION_POPUP_LIMIT:
920            mode = TR_SPEEDLIMIT_SINGLE;
921            break;
922        case OPTION_POPUP_NO_LIMIT:
923            mode = TR_SPEEDLIMIT_UNLIMITED;
924            break;
925        case OPTION_POPUP_GLOBAL:
926            mode = TR_SPEEDLIMIT_GLOBAL;
927            break;
928        default:
929            return;
930    }
931   
932    Torrent * torrent;
933    NSEnumerator * enumerator = [fTorrents objectEnumerator];
934    while ((torrent = [enumerator nextObject]))
935        [torrent setSpeedMode: mode upload: upload];
936   
937    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
938   
939    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
940    [field setHidden: !single];
941    if (single)
942    {
943        [field selectText: self];
944        [[self window] makeKeyAndOrderFront:self];
945    }
946   
947    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
948    [label setHidden: !single];
949}
950
951- (void) setSpeedLimit: (id) sender
952{
953    BOOL upload = sender == fUploadLimitField;
954    int limit = [sender intValue];
955   
956    Torrent * torrent;
957    NSEnumerator * enumerator = [fTorrents objectEnumerator];
958   
959    while ((torrent = [enumerator nextObject]))
960        [torrent setSpeedLimit: limit upload: upload];
961}
962
963- (void) setRatioSetting: (id) sender
964{
965    int setting;
966    switch ([sender indexOfSelectedItem])
967    {
968        case OPTION_POPUP_LIMIT:
969            setting = NSOnState;
970            break;
971        case OPTION_POPUP_NO_LIMIT:
972            setting = NSOffState;
973            break;
974        case OPTION_POPUP_GLOBAL:
975            setting = NSMixedState;
976            break;
977        default:
978            return;
979    }
980   
981    Torrent * torrent;
982    NSEnumerator * enumerator = [fTorrents objectEnumerator];
983    while ((torrent = [enumerator nextObject]))
984        [torrent setRatioSetting: setting];
985   
986    BOOL single = setting == NSOnState;
987    [fRatioLimitField setHidden: !single];
988    if (single)
989    {
990        [fRatioLimitField selectText: self];
991        [[self window] makeKeyAndOrderFront:self];
992    }
993   
994    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
995}
996
997- (void) setRatioLimit: (id) sender
998{
999    float limit = [sender floatValue];
1000   
1001    Torrent * torrent;
1002    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1003    while ((torrent = [enumerator nextObject]))
1004        [torrent setRatioLimit: limit];
1005}
1006
1007- (void) setPeersConnectLimit: (id) sender
1008{
1009    int limit = [sender intValue];
1010   
1011    Torrent * torrent;
1012    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1013    while ((torrent = [enumerator nextObject]))
1014        [torrent setMaxPeerConnect: limit];
1015}
1016
1017
1018- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
1019{
1020    [fInitialString release];
1021    fInitialString = [[control stringValue] retain];
1022   
1023    return YES;
1024}
1025
1026- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
1027{
1028    NSBeep();
1029    if (fInitialString)
1030    {
1031        [control setStringValue: fInitialString];
1032        [fInitialString release];
1033        fInitialString = nil;
1034    }
1035    return NO;
1036}
1037
1038@end
1039
1040@implementation InfoWindowController (Private)
1041
1042- (void) updateInfoGeneral
1043{   
1044    if ([fTorrents count] != 1)
1045        return;
1046   
1047    Torrent * torrent = [fTorrents objectAtIndex: 0];
1048   
1049    [fTrackerField setStringValue: [torrent trackerAddressAnnounce]];
1050   
1051    NSString * location = [torrent dataLocation];
1052    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1053    [fDataLocationField setToolTip: location];
1054}
1055
1056- (void) updateInfoActivity
1057{
1058    int numberSelected = [fTorrents count];
1059    if (numberSelected == 0)
1060        return;
1061   
1062    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1063    NSDate * lastActivity = nil;
1064    Torrent * torrent;
1065    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1066    while ((torrent = [enumerator nextObject]))
1067    {
1068        have += [torrent haveTotal];
1069        haveVerified += [torrent haveVerified];
1070        downloadedTotal += [torrent downloadedTotal];
1071        uploadedTotal += [torrent uploadedTotal];
1072        failedHash += [torrent failedHash];
1073       
1074        NSDate * nextLastActivity;
1075        if ((nextLastActivity = [torrent dateActivity]))
1076            lastActivity = lastActivity ? [lastActivity laterDate: nextLastActivity] : nextLastActivity;
1077    }
1078   
1079    if (have == 0)
1080        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1081    else
1082    {
1083        NSString * verifiedString = [NSString stringWithFormat: NSLocalizedString(@"%@ verified", "Inspector -> Activity tab -> have"),
1084                                        [NSString stringForFileSize: haveVerified]];
1085        if (have == haveVerified)
1086            [fHaveField setStringValue: verifiedString];
1087        else
1088            [fHaveField setStringValue: [NSString stringWithFormat: @"%@ (%@)", [NSString stringForFileSize: have], verifiedString]];
1089    }
1090   
1091    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1092    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1093    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1094   
1095    [fDateActivityField setObjectValue: lastActivity];
1096   
1097    if (numberSelected == 1)
1098    {
1099        torrent = [fTorrents objectAtIndex: 0];
1100       
1101        [fStateField setStringValue: [torrent stateString]];
1102       
1103        if ([torrent folder])
1104            [fProgressField setStringValue: [NSString localizedStringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1105                "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1106        else
1107            [fProgressField setStringValue: [NSString localizedStringWithFormat: @"%.2f%%", 100.0 * [torrent progress]]];
1108           
1109        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1110        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1111       
1112        NSString * errorMessage = [torrent errorMessage];
1113        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1114        {
1115            [fErrorMessageView setString: errorMessage];
1116            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1117        }
1118       
1119        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1120       
1121        [fPiecesView updateView: NO];
1122    }
1123}
1124
1125- (void) updateInfoTracker
1126{
1127    if ([fTorrents count] != 1)
1128        return;
1129    Torrent * torrent = [fTorrents objectAtIndex: 0];
1130   
1131    //announce fields
1132    NSString * announceAddress = [torrent trackerAddressAnnounce];
1133    [fAnnounceAddressField setStringValue: announceAddress];
1134    [fAnnounceAddressField setToolTip: announceAddress];
1135   
1136    [fAnnounceLastField setObjectValue: [torrent lastAnnounceTime]];
1137   
1138    NSString * announceResponse = [torrent announceResponse];
1139    [fAnnounceResponseField setStringValue: announceResponse];
1140    [fAnnounceResponseField setToolTip: announceResponse];
1141    [fAnnounceResponseField setSelectable: ![announceResponse isEqualToString: @""]];
1142   
1143    int announceNext = [torrent nextAnnounceTime];
1144    [fAnnounceNextField setStringValue: announceNext > 0 ? [NSString timeString: announceNext showSeconds: YES] : @""];
1145   
1146    //scrape fields
1147    NSString * scrapeAddress;
1148    if ((scrapeAddress = [torrent trackerAddressScrape]))
1149    {
1150        [fScrapeAddressField setStringValue: scrapeAddress];
1151        [fScrapeAddressField setToolTip: scrapeAddress];
1152    }
1153    else
1154    {
1155        [fScrapeAddressField setStringValue: @""];
1156        [fScrapeAddressField setToolTip: @""];
1157    }
1158   
1159    [fScrapeLastField setObjectValue: [torrent lastScrapeTime]];
1160   
1161    NSString * scrapeResponse = [torrent scrapeResponse];
1162    [fScrapeResponseField setStringValue: scrapeResponse];
1163    [fScrapeResponseField setToolTip: scrapeResponse];
1164    [fScrapeResponseField setSelectable: ![scrapeResponse isEqualToString: @""]];
1165   
1166    int scrapeNext = [torrent nextScrapeTime];
1167    [fScrapeNextField setStringValue: scrapeNext > 0 ? [NSString timeString: scrapeNext showSeconds: YES] : @""];
1168}
1169
1170- (void) updateInfoPeers
1171{
1172    if ([fTorrents count] != 1)
1173        return;
1174    Torrent * torrent = [fTorrents objectAtIndex: 0];
1175   
1176    int seeders = [torrent seeders], leechers = [torrent leechers], completed = [torrent completedFromTracker];
1177    [fSeedersField setStringValue: seeders >= 0 ? [NSString stringWithFormat: @"%d", seeders] : @""];
1178    [fLeechersField setStringValue: leechers >= 0 ? [NSString stringWithFormat: @"%d", leechers] : @""];
1179    [fCompletedFromTrackerField setStringValue: completed >= 0 ? [NSString stringWithFormat: @"%d", completed] : @""];
1180   
1181    BOOL active = [torrent isActive];
1182   
1183    if (active)
1184    {
1185        int total = [torrent totalPeersConnected];
1186        NSString * connected = [NSString stringWithFormat:
1187                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1188       
1189        if (total > 0)
1190        {
1191            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
1192            int count;
1193            if ((count = [torrent totalPeersTracker]) > 0)
1194                [components addObject: [NSString stringWithFormat:
1195                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1196            if ((count = [torrent totalPeersIncoming]) > 0)
1197                [components addObject: [NSString stringWithFormat:
1198                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1199            if ((count = [torrent totalPeersPex]) > 0)
1200                [components addObject: [NSString stringWithFormat:
1201                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1202            if ((count = [torrent totalPeersCache]) > 0)
1203                [components addObject: [NSString stringWithFormat:
1204                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1205           
1206            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1207        }
1208       
1209        [fConnectedPeersField setStringValue: connected];
1210       
1211        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1212        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1213    }
1214    else
1215    {
1216        [fConnectedPeersField setStringValue: @""];
1217        [fDownloadingFromField setStringValue: @""];
1218        [fUploadingToField setStringValue: @""];
1219    }
1220   
1221    [fKnownField setIntValue: [torrent totalPeersKnown]];
1222   
1223    [fPeers release];
1224    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1225   
1226    [fPeerTable reloadData];
1227}
1228
1229- (void) updateInfoFiles
1230{
1231    if ([fTorrents count] == 1)
1232        [fFileController reloadData];
1233}
1234
1235- (NSView *) tabViewForTag: (int) tag
1236{
1237    switch (tag)
1238    {
1239        case TAB_INFO_TAG:
1240            return fInfoView;
1241        case TAB_ACTIVITY_TAG:
1242            return fActivityView;
1243        case TAB_TRACKER_TAG:
1244            return fTrackerView;
1245        case TAB_PEERS_TAG:
1246            return fPeersView;
1247        case TAB_FILES_TAG:
1248            return fFilesView;
1249        case TAB_OPTIONS_TAG:
1250            return fOptionsView;
1251        default:
1252            return nil;
1253    }
1254}
1255
1256- (NSArray *) peerSortDescriptors
1257{
1258    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1259   
1260    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1261    BOOL useSecond = YES, asc = YES;
1262    if ([oldDescriptors count] > 0)
1263    {
1264        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1265        [descriptors addObject: descriptor];
1266       
1267        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1268            asc = [descriptor ascending];
1269    }
1270   
1271    //sort by IP after primary sort
1272    if (useSecond)
1273    {
1274        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1275                                                                        selector: @selector(compareIP:)];
1276        [descriptors addObject: secondDescriptor];
1277        [secondDescriptor release];
1278    }
1279   
1280    return descriptors;
1281}
1282
1283@end
Note: See TracBrowser for help on using the repository browser.