source: trunk/macosx/InfoWindowController.m @ 5862

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

#958 QuickLook? from within the Inspector - thanks Waldorf!

  • Property svn:keywords set to Date Rev Author Id
File size: 47.0 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 5862 2008-05-20 19:34:33Z 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: [[torrent publicTorrentLocation] stringByAppendingFormat: @"\n\n%@",
398                                                [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) windowWillClose: (NSNotification *) notification
594{
595    [fFileController fileTabClosed];
596}
597
598- (void) setTab: (id) sender
599{
600    int oldTabTag = fCurrentTabTag;
601    fCurrentTabTag = [fTabMatrix selectedTag];
602    if (fCurrentTabTag == oldTabTag)
603        return;
604   
605    [self updateInfoStats];
606   
607    //take care of old view
608    float oldHeight = 0;
609    NSString * oldResizeSaveKey = nil;
610    if (oldTabTag != INVALID)
611    {
612        //deselect old tab item
613        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
614       
615        switch (oldTabTag)
616        {
617            case TAB_TRACKER_TAG:
618                oldResizeSaveKey = @"InspectorContentHeightTracker";
619                break;
620            case TAB_PEERS_TAG:
621                [fPeers release];
622                fPeers = nil;
623               
624                oldResizeSaveKey = @"InspectorContentHeightPeers";
625                break;
626            case TAB_FILES_TAG:
627                [fFileController fileTabClosed];
628               
629                oldResizeSaveKey = @"InspectorContentHeightFiles";
630                break;
631        }
632       
633        NSView * oldView = [self tabViewForTag: oldTabTag];
634        oldHeight = [oldView frame].size.height;
635        if (oldResizeSaveKey)
636            [[NSUserDefaults standardUserDefaults] setFloat: oldHeight forKey: oldResizeSaveKey];
637       
638        //remove old view
639        [oldView setHidden: YES];
640        [oldView removeFromSuperview];
641    }
642   
643    //set new tab item
644    NSView * view = [self tabViewForTag: fCurrentTabTag];
645   
646    NSString * resizeSaveKey = nil;
647    NSString * identifier, * title;
648    switch (fCurrentTabTag)
649    {
650        case TAB_INFO_TAG:
651            identifier = TAB_INFO_IDENT;
652            title = NSLocalizedString(@"General Info", "Inspector -> title");
653            break;
654        case TAB_ACTIVITY_TAG:
655            identifier = TAB_ACTIVITY_IDENT;
656            title = NSLocalizedString(@"Activity", "Inspector -> title");
657           
658            [fPiecesView updateView: YES];
659            break;
660        case TAB_TRACKER_TAG:
661            identifier = TAB_TRACKER_IDENT;
662            title = NSLocalizedString(@"Tracker", "Inspector -> title");
663            resizeSaveKey = @"InspectorContentHeightTracker";
664            break;
665        case TAB_PEERS_TAG:
666            identifier = TAB_PEERS_IDENT;
667            title = NSLocalizedString(@"Peers", "Inspector -> title");
668            resizeSaveKey = @"InspectorContentHeightPeers";
669            break;
670        case TAB_FILES_TAG:
671            identifier = TAB_FILES_IDENT;
672            title = NSLocalizedString(@"Files", "Inspector -> title");
673            resizeSaveKey = @"InspectorContentHeightFiles";
674            break;
675        case TAB_OPTIONS_TAG:
676            identifier = TAB_OPTIONS_IDENT;
677            title = NSLocalizedString(@"Options", "Inspector -> title");
678            break;
679        default:
680            return;
681    }
682   
683    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
684   
685    NSWindow * window = [self window];
686   
687    [window setTitle: [NSString stringWithFormat: @"%@ - %@", title, NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]];
688   
689    //selected tab item
690    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
691   
692    NSRect windowRect = [window frame], viewRect = [view frame];
693   
694    if (resizeSaveKey)
695    {
696        float height = [[NSUserDefaults standardUserDefaults] floatForKey: resizeSaveKey];
697        if (height != 0)
698            viewRect.size.height = MAX(height, TAB_MIN_HEIGHT);
699    }
700   
701    float difference = (viewRect.size.height - oldHeight) * [window userSpaceScaleFactor];
702    windowRect.origin.y -= difference;
703    windowRect.size.height += difference;
704   
705    if (resizeSaveKey)
706    {
707        if (!oldResizeSaveKey)
708        {
709            [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height - viewRect.size.height + TAB_MIN_HEIGHT)];
710            [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
711        }
712    }
713    else
714    {
715        [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height)];
716        [window setMaxSize: NSMakeSize(FLT_MAX, windowRect.size.height)];
717    }
718   
719    viewRect.size.width = windowRect.size.width;
720    [view setFrame: viewRect];
721   
722    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
723    [[window contentView] addSubview: view];
724    [view setHidden: NO];
725}
726
727- (void) setNextTab
728{
729    int tag = [fTabMatrix selectedTag]+1;
730    if (tag >= [fTabMatrix numberOfColumns])
731        tag = 0;
732   
733    [fTabMatrix selectCellWithTag: tag];
734    [self setTab: nil];
735}
736
737- (void) setPreviousTab
738{
739    int tag = [fTabMatrix selectedTag]-1;
740    if (tag < 0)
741        tag = [fTabMatrix numberOfColumns]-1;
742   
743    [fTabMatrix selectCellWithTag: tag];
744    [self setTab: nil];
745}
746
747- (int) numberOfRowsInTableView: (NSTableView *) tableView
748{
749    if (tableView == fPeerTable)
750        return fPeers ? [fPeers count] : 0;
751    else if (tableView == fTrackerTable)
752        return fTrackers ? [fTrackers count] : 0;
753    return 0;
754}
755
756- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
757{
758    if (tableView == fPeerTable)
759    {
760        NSString * ident = [column identifier];
761        NSDictionary * peer = [fPeers objectAtIndex: row];
762       
763        if ([ident isEqualToString: @"Encryption"])
764            return [[peer objectForKey: @"Encryption"] boolValue] ? [NSImage imageNamed: @"Lock.png"] : nil;
765        else if ([ident isEqualToString: @"Client"])
766            return [peer objectForKey: @"Client"];
767        else if  ([ident isEqualToString: @"Progress"])
768            return [peer objectForKey: @"Progress"];
769        else if ([ident isEqualToString: @"UL To"])
770        {
771            NSNumber * rate;
772            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
773        }
774        else if ([ident isEqualToString: @"DL From"])
775        {
776            NSNumber * rate;
777            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
778        }
779        else
780            return [peer objectForKey: @"IP"];
781    }
782    else if (tableView == fTrackerTable)
783    {
784        id item = [fTrackers objectAtIndex: row];
785        if ([item isKindOfClass: [NSNumber class]])
786            return [NSString stringWithFormat: NSLocalizedString(@"Tier %d", "Inspector -> tracker table"), [item intValue]+1];
787        else
788            return item;
789    }
790    return nil;
791}
792
793- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
794{
795    if (tableView == fPeerTable)
796    {
797        if (fPeers)
798        {
799            NSArray * oldPeers = fPeers;
800            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
801            [oldPeers release];
802            [tableView reloadData];
803        }
804    }
805}
806
807- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) row
808{
809    return NO;
810}
811
812- (BOOL) tableView: (NSTableView *) tableView isGroupRow: (NSInteger) row
813{
814    if (tableView == fTrackerTable)
815        return [[fTrackers objectAtIndex: row] isKindOfClass: [NSNumber class]];
816    return NO;
817}
818
819- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
820                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
821{
822    if (tableView == fPeerTable)
823    {
824        NSDictionary * peer = [fPeers objectAtIndex: row];
825        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 5];
826       
827        [components addObject: [NSString localizedStringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
828            "Inspector -> Peers tab -> table row tooltip"), [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
829       
830        if ([[peer objectForKey: @"Encryption"] boolValue])
831            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
832       
833        NSString * portString;
834        int port;
835        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
836            portString = [NSString stringWithFormat: @"%d", port];
837        else
838            portString = NSLocalizedString(@"N/A", "Inspector -> Peers tab -> table row tooltip");
839        [components addObject: [NSString stringWithFormat: @"%@: %@", NSLocalizedString(@"Port",
840            "Inspector -> Peers tab -> table row tooltip"), portString]];
841       
842        switch ([[peer objectForKey: @"From"] intValue])
843        {
844            case TR_PEER_FROM_TRACKER:
845                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
846                break;
847            case TR_PEER_FROM_INCOMING:
848                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
849                break;
850            case TR_PEER_FROM_CACHE:
851                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
852                break;
853            case TR_PEER_FROM_PEX:
854                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
855                break;
856        }
857       
858        //determing status strings from flags
859        NSMutableArray * statusArray = [NSMutableArray arrayWithCapacity: 3];
860        NSString * flags = [peer objectForKey: @"Flags"];
861       
862        if ([flags rangeOfString: @"D"].location != NSNotFound)
863            [statusArray addObject: NSLocalizedString(@"Currently downloading (interested and not choked)",
864                "Inspector -> peer -> status")];
865        else if ([flags rangeOfString: @"d"].location != NSNotFound)
866            [statusArray addObject: NSLocalizedString(@"You want to download, but peer does not want to send (interested and choked)",
867                "Inspector -> peer -> status")];
868        else;
869       
870        if ([flags rangeOfString: @"U"].location != NSNotFound)
871            [statusArray addObject: NSLocalizedString(@"Currently uploading (interested and not choked)",
872                "Inspector -> peer -> status")];
873        else if ([flags rangeOfString: @"u"].location != NSNotFound)
874            [statusArray addObject: NSLocalizedString(@"Peer wants you to upload, but you do not want to (interested and choked)",
875                "Inspector -> peer -> status")];
876        else;
877       
878        if ([flags rangeOfString: @"K"].location != NSNotFound)
879            [statusArray addObject: NSLocalizedString(@"Peer is unchoking you, but you are not interested",
880                "Inspector -> peer -> status")];
881       
882        if ([flags rangeOfString: @"?"].location != NSNotFound)
883            [statusArray addObject: NSLocalizedString(@"You unchoked the peer, but the peer is not interested",
884                "Inspector -> peer -> status")];
885       
886        if ([statusArray count] > 0)
887            [components addObject: [@"\n" stringByAppendingString: [statusArray componentsJoinedByString: @"\n\n"]]];
888       
889        return [components componentsJoinedByString: @"\n"];
890    }
891    return nil;
892}
893
894- (void) setPiecesView: (id) sender
895{
896    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
897}
898
899- (void) setPiecesViewForAvailable: (BOOL) available
900{
901    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
902    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
903   
904    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
905    [fPiecesView updateView: YES];
906}
907
908- (void) revealTorrentFile: (id) sender
909{
910    if ([fTorrents count] > 0)
911        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
912}
913
914- (void) revealDataFile: (id) sender
915{
916    if ([fTorrents count] > 0)
917        [[fTorrents objectAtIndex: 0] revealData];
918}
919
920- (void) setSpeedMode: (id) sender
921{
922    BOOL upload = sender == fUploadLimitPopUp;
923    int mode;
924    switch ([sender indexOfSelectedItem])
925    {
926        case OPTION_POPUP_LIMIT:
927            mode = TR_SPEEDLIMIT_SINGLE;
928            break;
929        case OPTION_POPUP_NO_LIMIT:
930            mode = TR_SPEEDLIMIT_UNLIMITED;
931            break;
932        case OPTION_POPUP_GLOBAL:
933            mode = TR_SPEEDLIMIT_GLOBAL;
934            break;
935        default:
936            return;
937    }
938   
939    Torrent * torrent;
940    NSEnumerator * enumerator = [fTorrents objectEnumerator];
941    while ((torrent = [enumerator nextObject]))
942        [torrent setSpeedMode: mode upload: upload];
943   
944    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
945   
946    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
947    [field setHidden: !single];
948    if (single)
949    {
950        [field selectText: self];
951        [[self window] makeKeyAndOrderFront:self];
952    }
953   
954    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
955    [label setHidden: !single];
956}
957
958- (void) setSpeedLimit: (id) sender
959{
960    BOOL upload = sender == fUploadLimitField;
961    int limit = [sender intValue];
962   
963    Torrent * torrent;
964    NSEnumerator * enumerator = [fTorrents objectEnumerator];
965   
966    while ((torrent = [enumerator nextObject]))
967        [torrent setSpeedLimit: limit upload: upload];
968}
969
970- (void) setRatioSetting: (id) sender
971{
972    int setting;
973    switch ([sender indexOfSelectedItem])
974    {
975        case OPTION_POPUP_LIMIT:
976            setting = NSOnState;
977            break;
978        case OPTION_POPUP_NO_LIMIT:
979            setting = NSOffState;
980            break;
981        case OPTION_POPUP_GLOBAL:
982            setting = NSMixedState;
983            break;
984        default:
985            return;
986    }
987   
988    Torrent * torrent;
989    NSEnumerator * enumerator = [fTorrents objectEnumerator];
990    while ((torrent = [enumerator nextObject]))
991        [torrent setRatioSetting: setting];
992   
993    BOOL single = setting == NSOnState;
994    [fRatioLimitField setHidden: !single];
995    if (single)
996    {
997        [fRatioLimitField selectText: self];
998        [[self window] makeKeyAndOrderFront:self];
999    }
1000   
1001    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1002}
1003
1004- (void) setRatioLimit: (id) sender
1005{
1006    float limit = [sender floatValue];
1007   
1008    Torrent * torrent;
1009    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1010    while ((torrent = [enumerator nextObject]))
1011        [torrent setRatioLimit: limit];
1012}
1013
1014- (void) setPeersConnectLimit: (id) sender
1015{
1016    int limit = [sender intValue];
1017   
1018    Torrent * torrent;
1019    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1020    while ((torrent = [enumerator nextObject]))
1021        [torrent setMaxPeerConnect: limit];
1022}
1023
1024
1025- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
1026{
1027    [fInitialString release];
1028    fInitialString = [[control stringValue] retain];
1029   
1030    return YES;
1031}
1032
1033- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
1034{
1035    NSBeep();
1036    if (fInitialString)
1037    {
1038        [control setStringValue: fInitialString];
1039        [fInitialString release];
1040        fInitialString = nil;
1041    }
1042    return NO;
1043}
1044
1045@end
1046
1047@implementation InfoWindowController (Private)
1048
1049- (void) updateInfoGeneral
1050{   
1051    if ([fTorrents count] != 1)
1052        return;
1053   
1054    Torrent * torrent = [fTorrents objectAtIndex: 0];
1055   
1056    [fTrackerField setStringValue: [torrent trackerAddressAnnounce]];
1057   
1058    NSString * location = [torrent dataLocation];
1059    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1060    [fDataLocationField setToolTip: location];
1061}
1062
1063- (void) updateInfoActivity
1064{
1065    int numberSelected = [fTorrents count];
1066    if (numberSelected == 0)
1067        return;
1068   
1069    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1070    NSDate * lastActivity = nil;
1071    Torrent * torrent;
1072    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1073    while ((torrent = [enumerator nextObject]))
1074    {
1075        have += [torrent haveTotal];
1076        haveVerified += [torrent haveVerified];
1077        downloadedTotal += [torrent downloadedTotal];
1078        uploadedTotal += [torrent uploadedTotal];
1079        failedHash += [torrent failedHash];
1080       
1081        NSDate * nextLastActivity;
1082        if ((nextLastActivity = [torrent dateActivity]))
1083            lastActivity = lastActivity ? [lastActivity laterDate: nextLastActivity] : nextLastActivity;
1084    }
1085   
1086    if (have == 0)
1087        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1088    else
1089    {
1090        NSString * verifiedString = [NSString stringWithFormat: NSLocalizedString(@"%@ verified", "Inspector -> Activity tab -> have"),
1091                                        [NSString stringForFileSize: haveVerified]];
1092        if (have == haveVerified)
1093            [fHaveField setStringValue: verifiedString];
1094        else
1095            [fHaveField setStringValue: [NSString stringWithFormat: @"%@ (%@)", [NSString stringForFileSize: have], verifiedString]];
1096    }
1097   
1098    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1099    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1100    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1101   
1102    [fDateActivityField setObjectValue: lastActivity];
1103   
1104    if (numberSelected == 1)
1105    {
1106        torrent = [fTorrents objectAtIndex: 0];
1107       
1108        [fStateField setStringValue: [torrent stateString]];
1109       
1110        if ([torrent folder])
1111            [fProgressField setStringValue: [NSString localizedStringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1112                "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1113        else
1114            [fProgressField setStringValue: [NSString localizedStringWithFormat: @"%.2f%%", 100.0 * [torrent progress]]];
1115           
1116        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1117        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1118       
1119        NSString * errorMessage = [torrent errorMessage];
1120        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1121        {
1122            [fErrorMessageView setString: errorMessage];
1123            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1124        }
1125       
1126        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1127       
1128        [fPiecesView updateView: NO];
1129    }
1130}
1131
1132#warning reload table when necessary?
1133- (void) updateInfoTracker
1134{
1135    if ([fTorrents count] != 1)
1136        return;
1137    Torrent * torrent = [fTorrents objectAtIndex: 0];
1138   
1139    //announce fields
1140    NSString * announceAddress = [torrent trackerAddressAnnounce];
1141    [fAnnounceAddressField setStringValue: announceAddress];
1142    [fAnnounceAddressField setToolTip: announceAddress];
1143   
1144    [fAnnounceLastField setObjectValue: [torrent lastAnnounceTime]];
1145   
1146    NSString * announceResponse = [torrent announceResponse];
1147    [fAnnounceResponseField setStringValue: announceResponse];
1148    [fAnnounceResponseField setToolTip: announceResponse];
1149    [fAnnounceResponseField setSelectable: ![announceResponse isEqualToString: @""]];
1150   
1151    int announceNext = [torrent nextAnnounceTime];
1152    [fAnnounceNextField setStringValue: announceNext > 0 ? [NSString timeString: announceNext showSeconds: YES] : @""];
1153   
1154    //scrape fields
1155    NSString * scrapeAddress;
1156    if ((scrapeAddress = [torrent trackerAddressScrape]))
1157    {
1158        [fScrapeAddressField setStringValue: scrapeAddress];
1159        [fScrapeAddressField setToolTip: scrapeAddress];
1160    }
1161    else
1162    {
1163        [fScrapeAddressField setStringValue: @""];
1164        [fScrapeAddressField setToolTip: @""];
1165    }
1166   
1167    [fScrapeLastField setObjectValue: [torrent lastScrapeTime]];
1168   
1169    NSString * scrapeResponse = [torrent scrapeResponse];
1170    [fScrapeResponseField setStringValue: scrapeResponse];
1171    [fScrapeResponseField setToolTip: scrapeResponse];
1172    [fScrapeResponseField setSelectable: ![scrapeResponse isEqualToString: @""]];
1173   
1174    int scrapeNext = [torrent nextScrapeTime];
1175    [fScrapeNextField setStringValue: scrapeNext > 0 ? [NSString timeString: scrapeNext showSeconds: YES] : @""];
1176}
1177
1178- (void) updateInfoPeers
1179{
1180    if ([fTorrents count] != 1)
1181        return;
1182    Torrent * torrent = [fTorrents objectAtIndex: 0];
1183   
1184    int seeders = [torrent seeders], leechers = [torrent leechers], completed = [torrent completedFromTracker];
1185    [fSeedersField setStringValue: seeders >= 0 ? [NSString stringWithFormat: @"%d", seeders] : @""];
1186    [fLeechersField setStringValue: leechers >= 0 ? [NSString stringWithFormat: @"%d", leechers] : @""];
1187    [fCompletedFromTrackerField setStringValue: completed >= 0 ? [NSString stringWithFormat: @"%d", completed] : @""];
1188   
1189    BOOL active = [torrent isActive];
1190   
1191    if (active)
1192    {
1193        int total = [torrent totalPeersConnected];
1194        NSString * connected = [NSString stringWithFormat:
1195                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1196       
1197        if (total > 0)
1198        {
1199            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
1200            int count;
1201            if ((count = [torrent totalPeersTracker]) > 0)
1202                [components addObject: [NSString stringWithFormat:
1203                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1204            if ((count = [torrent totalPeersIncoming]) > 0)
1205                [components addObject: [NSString stringWithFormat:
1206                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1207            if ((count = [torrent totalPeersPex]) > 0)
1208                [components addObject: [NSString stringWithFormat:
1209                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1210            if ((count = [torrent totalPeersCache]) > 0)
1211                [components addObject: [NSString stringWithFormat:
1212                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1213           
1214            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1215        }
1216       
1217        [fConnectedPeersField setStringValue: connected];
1218       
1219        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1220        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1221    }
1222    else
1223    {
1224        [fConnectedPeersField setStringValue: @""];
1225        [fDownloadingFromField setStringValue: @""];
1226        [fUploadingToField setStringValue: @""];
1227    }
1228   
1229    [fKnownField setIntValue: [torrent totalPeersKnown]];
1230   
1231    [fPeers release];
1232    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1233   
1234    [fPeerTable reloadData];
1235}
1236
1237- (void) updateInfoFiles
1238{
1239    if ([fTorrents count] == 1)
1240        [fFileController reloadData];
1241}
1242
1243- (NSView *) tabViewForTag: (int) tag
1244{
1245    switch (tag)
1246    {
1247        case TAB_INFO_TAG:
1248            return fInfoView;
1249        case TAB_ACTIVITY_TAG:
1250            return fActivityView;
1251        case TAB_TRACKER_TAG:
1252            return fTrackerView;
1253        case TAB_PEERS_TAG:
1254            return fPeersView;
1255        case TAB_FILES_TAG:
1256            return fFilesView;
1257        case TAB_OPTIONS_TAG:
1258            return fOptionsView;
1259        default:
1260            return nil;
1261    }
1262}
1263
1264- (NSArray *) peerSortDescriptors
1265{
1266    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1267   
1268    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1269    BOOL useSecond = YES, asc = YES;
1270    if ([oldDescriptors count] > 0)
1271    {
1272        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1273        [descriptors addObject: descriptor];
1274       
1275        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1276            asc = [descriptor ascending];
1277    }
1278   
1279    //sort by IP after primary sort
1280    if (useSecond)
1281    {
1282        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1283                                                                        selector: @selector(compareIP:)];
1284        [descriptors addObject: secondDescriptor];
1285        [secondDescriptor release];
1286    }
1287   
1288    return descriptors;
1289}
1290
1291@end
Note: See TracBrowser for help on using the repository browser.