source: trunk/macosx/InfoWindowController.m @ 3438

Last change on this file since 3438 was 3438, checked in by livings124, 15 years ago

fix regression where N/A is shown when leechers, seeders, or completed is 0; promote AR

  • Property svn:keywords set to Date Rev Author Id
File size: 51.1 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 3438 2007-10-17 00:46:20Z livings124 $
3 *
4 * Copyright (c) 2006-2007 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 "NSStringAdditions.h"
28
29#define FILE_ROW_SMALL_HEIGHT 18.0
30
31#define TAB_INFO_IDENT @"Info"
32#define TAB_ACTIVITY_IDENT @"Activity"
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,
52    TAB_PEERS_TAG,
53    TAB_FILES_TAG,
54    TAB_OPTIONS_TAG,
55} tabTag;
56
57@interface InfoWindowController (Private)
58
59- (void) updateInfoGeneral;
60- (void) updateInfoActivity;
61- (void) updateInfoPeers;
62- (void) updateInfoFiles;
63- (void) updateInfoOptions;
64
65- (NSView *) tabViewForTag: (int) tag;
66- (NSArray *) peerSortDescriptors;
67
68@end
69
70@implementation InfoWindowController
71
72- (id) init
73{
74    return [super initWithWindowNibName: @"InfoWindow"];
75}
76
77- (void) awakeFromNib
78{
79    //get images
80    fAppIcon = [NSImage imageNamed: @"NSApplicationIcon"];
81   
82    //window location and size
83    NSPanel * window = (NSPanel *)[self window];
84   
85    float windowHeight = [window frame].size.height;
86   
87    [window setFrameAutosaveName: @"InspectorWindow"];
88    [window setFrameUsingName: @"InspectorWindow"];
89   
90    NSRect windowRect = [window frame];
91    windowRect.origin.y -= windowHeight - windowRect.size.height;
92    windowRect.size.height = windowHeight;
93    [window setFrame: windowRect display: NO];
94   
95    [window setBecomesKeyOnlyIfNeeded: YES];
96    [window setAcceptsMouseMovedEvents: YES];
97   
98    //set tab images and tooltips
99    [fTabBackBar setBackgroundImage: [NSImage imageNamed: @"InfoTabBorder.tif"]];
100    [[fTabMatrix cellWithTag: TAB_INFO_TAG] setIcon: [NSImage imageNamed: @"InfoGeneral.png"]];
101    [[fTabMatrix cellWithTag: TAB_ACTIVITY_TAG] setIcon: [NSImage imageNamed: @"InfoActivity.png"]];
102    [[fTabMatrix cellWithTag: TAB_PEERS_TAG] setIcon: [NSImage imageNamed: @"InfoPeers.png"]];
103    [[fTabMatrix cellWithTag: TAB_FILES_TAG] setIcon: [NSImage imageNamed: @"InfoFiles.png"]];
104    [[fTabMatrix cellWithTag: TAB_OPTIONS_TAG] setIcon: [NSImage imageNamed: @"InfoOptions.png"]];
105   
106    [fTabMatrix setToolTip: NSLocalizedString(@"General Info", "Inspector -> tab tooltip")
107                    forCell: [fTabMatrix cellWithTag: TAB_INFO_TAG]];
108    [fTabMatrix setToolTip: NSLocalizedString(@"Activity", "Inspector -> tab tooltip")
109                    forCell: [fTabMatrix cellWithTag: TAB_ACTIVITY_TAG]];
110    [fTabMatrix setToolTip: NSLocalizedString(@"Peers", "Inspector -> tab tooltip")
111                    forCell: [fTabMatrix cellWithTag:  TAB_PEERS_TAG]];
112    [fTabMatrix setToolTip: NSLocalizedString(@"Files", "Inspector -> tab tooltip")
113                    forCell: [fTabMatrix cellWithTag: TAB_FILES_TAG]];
114    [fTabMatrix setToolTip: NSLocalizedString(@"Options", "Inspector -> tab tooltip")
115                    forCell: [fTabMatrix cellWithTag: TAB_OPTIONS_TAG]];
116   
117    //set selected tab
118    fCurrentTabTag = INVALID;
119    NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"];
120    int tag;
121    if ([identifier isEqualToString: TAB_INFO_IDENT])
122        tag = TAB_INFO_TAG;
123    else if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
124        tag = TAB_ACTIVITY_TAG;
125    else if ([identifier isEqualToString: TAB_PEERS_IDENT])
126        tag = TAB_PEERS_TAG;
127    else if ([identifier isEqualToString: TAB_FILES_IDENT])
128        tag = TAB_FILES_TAG;
129    else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
130        tag = TAB_OPTIONS_TAG;
131    else //safety
132    {
133        [[NSUserDefaults standardUserDefaults] setObject: TAB_INFO_IDENT forKey: @"InspectorSelected"];
134        tag = TAB_INFO_TAG;
135    }
136    [fTabMatrix selectCellWithTag: tag];
137    [self setTab: nil];
138   
139    //initially sort peer table by IP
140    if ([[fPeerTable sortDescriptors] count] == 0)
141        [fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
142                                            sortDescriptorPrototype]]];
143   
144    //set file table
145    [fFileOutline setDoubleAction: @selector(revealFile:)];
146   
147    //set priority item images
148    [fFilePriorityNormal setImage: [NSImage imageNamed: @"PriorityNormal.png"]];
149    [fFilePriorityLow setImage: [NSImage imageNamed: @"PriorityLow.png"]];
150    [fFilePriorityHigh setImage: [NSImage imageNamed: @"PriorityHigh.png"]];
151   
152    //set blank inspector
153    [self updateInfoForTorrents: [NSArray array]];
154   
155    //allow for update notifications
156    NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
157    [nc addObserver: self selector: @selector(updateInfoStats) name: @"UpdateStats" object: nil];
158}
159
160- (void) dealloc
161{
162    //save resizeable view height
163    if (fCurrentTabTag == TAB_PEERS_TAG || fCurrentTabTag == TAB_FILES_TAG)
164        [[NSUserDefaults standardUserDefaults] setFloat: [[self tabViewForTag: fCurrentTabTag] frame].size.height
165            forKey: @"InspectorContentHeight"];
166   
167    [[NSNotificationCenter defaultCenter] removeObserver: self];
168   
169    [fTorrents release];
170    [fPeers release];
171    [fFiles release];
172   
173    [super dealloc];
174}
175
176- (void) updateInfoForTorrents: (NSArray *) torrents
177{
178    [fTorrents release];
179    fTorrents = [torrents retain];
180
181    int numberSelected = [fTorrents count];
182    if (numberSelected != 1)
183    {
184        if (numberSelected > 0)
185        {
186            [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d Torrents Selected",
187                                            "Inspector -> above tabs -> selected torrents"), numberSelected]];
188       
189            uint64_t size = 0;
190            NSEnumerator * enumerator = [torrents objectEnumerator];
191            Torrent * torrent;
192            while ((torrent = [enumerator nextObject]))
193                size += [torrent size];
194           
195            [fSizeField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ Total",
196                "Inspector -> above tabs -> total size (several torrents selected)"), [NSString stringForFileSize: size]]];
197        }
198        else
199        {
200            [fNameField setStringValue: NSLocalizedString(@"No Torrents Selected", "Inspector -> above tabs -> selected torrents")];
201            [fSizeField setStringValue: @""];
202   
203            [fHaveField setStringValue: @""];
204            [fDownloadedTotalField setStringValue: @""];
205            [fUploadedTotalField setStringValue: @""];
206            [fFailedHashField setStringValue: @""];
207           
208            //options fields
209            [fUploadLimitPopUp setEnabled: NO];
210            [fUploadLimitPopUp selectItemAtIndex: -1];
211            [fUploadLimitField setHidden: YES];
212            [fUploadLimitLabel setHidden: YES];
213            [fUploadLimitField setStringValue: @""];
214           
215            [fDownloadLimitPopUp setEnabled: NO];
216            [fDownloadLimitPopUp selectItemAtIndex: -1];
217            [fDownloadLimitField setHidden: YES];
218            [fDownloadLimitLabel setHidden: YES];
219            [fDownloadLimitField setStringValue: @""];
220           
221            [fRatioPopUp setEnabled: NO];
222            [fRatioPopUp selectItemAtIndex: -1];
223            [fRatioLimitField setHidden: YES];
224            [fRatioLimitField setStringValue: @""];
225           
226            [fPexCheck setEnabled: NO];
227            [fPexCheck setState: NSOffState];
228            [fPexCheck setToolTip: nil];
229        }
230       
231        [fImageView setImage: fAppIcon];
232       
233        [fNameField setToolTip: nil];
234
235        [fTrackerField setStringValue: @""];
236        [fTrackerField setToolTip: nil];
237        [fPiecesField setStringValue: @""];
238        [fHashField setStringValue: @""];
239        [fHashField setToolTip: nil];
240        [fSecureField setStringValue: @""];
241        [fCommentView setString: @""];
242       
243        [fCreatorField setStringValue: @""];
244        [fDateCreatedField setStringValue: @""];
245        [fCommentView setSelectable: NO];
246       
247        [fTorrentLocationField setStringValue: @""];
248        [fTorrentLocationField setToolTip: nil];
249        [fDataLocationField setStringValue: @""];
250        [fDataLocationField setToolTip: nil];
251       
252        [fRevealDataButton setHidden: YES];
253        [fRevealTorrentButton setHidden: YES];
254       
255        //don't allow empty fields to be selected
256        [fTrackerField setSelectable: NO];
257        [fHashField setSelectable: NO];
258        [fCreatorField setSelectable: NO];
259        [fTorrentLocationField setSelectable: NO];
260        [fDataLocationField setSelectable: NO];
261       
262        [fStateField setStringValue: @""];
263        [fProgressField setStringValue: @""];
264        [fRatioField setStringValue: @""];
265       
266        [fSwarmSpeedField setStringValue: @""];
267        [fErrorMessageView setString: @""];
268        [fErrorMessageView setSelectable: NO];
269       
270        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
271        [fDownloadingFromField setStringValue: @""];
272        [fUploadingToField setStringValue: @""];
273        [fKnownField setStringValue: @""];
274        [fSeedersField setStringValue: @""];
275        [fLeechersField setStringValue: @""];
276        [fCompletedFromTrackerField setStringValue: @""];
277       
278        [fDateAddedField setStringValue: @""];
279        [fDateCompletedField setStringValue: @""];
280        [fDateActivityField setStringValue: @""];
281       
282        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE];
283        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS];
284        [fPiecesControl setEnabled: NO];
285        [fPiecesView setTorrent: nil];
286       
287        if (fPeers)
288        {
289            [fPeers release];
290            fPeers = nil;
291        }
292       
293        if (fFiles)
294        {
295            [fFiles release];
296            fFiles = nil;
297        }
298        [fFileTableStatusField setStringValue: NSLocalizedString(@"info not available",
299                                        "Inspector -> Files tab -> bottom text (number of files)")];
300    }
301    else
302    {   
303        Torrent * torrent = [fTorrents objectAtIndex: 0];
304       
305        NSImage * icon = [[torrent icon] copy];
306        [icon setFlipped: NO];
307        [fImageView setImage: icon];
308        [icon release];
309       
310        NSString * name = [torrent name];
311        [fNameField setStringValue: name];
312        [fNameField setToolTip: name];
313        [fSizeField setStringValue: [NSString stringForFileSize: [torrent size]]];
314       
315        NSString * hashString = [torrent hashString];
316        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
317                                        [NSString stringForFileSize: [torrent pieceSize]]]];
318        [fHashField setStringValue: hashString];
319        [fHashField setToolTip: hashString];
320        [fSecureField setStringValue: [torrent privateTorrent]
321                        ? NSLocalizedString(@"Private Torrent, PEX disabled", "Inspector -> is private torrent")
322                        : NSLocalizedString(@"Public Torrent", "Inspector -> is not private torrent")];
323       
324        NSString * commentString = [torrent comment];
325        [fCommentView setString: commentString];
326       
327        NSString * creatorString = [torrent creator];
328        [fCreatorField setStringValue: creatorString];
329        [fDateCreatedField setObjectValue: [torrent dateCreated]];
330       
331        BOOL publicTorrent = [torrent publicTorrent];
332        [fTorrentLocationField setStringValue: publicTorrent
333                    ? [[torrent publicTorrentLocation] stringByAbbreviatingWithTildeInPath]
334                    : NSLocalizedString(@"Transmission Support Folder", "Torrent -> location when deleting original")];
335        if (publicTorrent)
336            [fTorrentLocationField setToolTip: [NSString stringWithFormat: @"%@\n\n%@",
337                        [torrent publicTorrentLocation], [torrent torrentLocation]]];
338        else
339            [fTorrentLocationField setToolTip: [torrent torrentLocation]];
340       
341        [fDateAddedField setObjectValue: [torrent dateAdded]];
342       
343        [fRevealDataButton setHidden: NO];
344        [fRevealTorrentButton setHidden: ![torrent publicTorrent]];
345       
346        //allow these fields to be selected
347        [fTrackerField setSelectable: YES];
348        [fHashField setSelectable: YES];
349        [fCommentView setSelectable: ![commentString isEqualToString: @""]];
350        [fCreatorField setSelectable: ![creatorString isEqualToString: @""]];
351        [fTorrentLocationField setSelectable: YES];
352        [fDataLocationField setSelectable: YES];
353       
354        //set pieces view
355        BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"];
356        [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE];
357        [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS];
358        [fPiecesControl setEnabled: YES];
359        [fPiecesView setTorrent: torrent];
360       
361        //set file table
362        [fFileOutline deselectAll: nil];
363        [fFiles release];
364        fFiles = [[torrent fileList] retain];
365       
366        int fileCount = [torrent fileCount];
367        if (fileCount != 1)
368            [fFileTableStatusField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d files total",
369                                "Inspector -> Files tab -> bottom text (number of files)"), fileCount]];
370        else
371            [fFileTableStatusField setStringValue: NSLocalizedString(@"1 file total",
372                                "Inspector -> Files tab -> bottom text (number of files)")];
373    }
374   
375    //update stats and settings
376    [self updateInfoStats];
377   
378    [fPeerTable reloadData];
379    [fFileOutline deselectAll: nil];
380    [fFileOutline reloadData];
381}
382
383- (Torrent *) selectedTorrent
384{
385    return fTorrents && [fTorrents count] == 1 ? [fTorrents objectAtIndex: 0] : nil;
386}
387
388- (void) updateInfoStats
389{
390    switch ([fTabMatrix selectedTag])
391    {
392        case TAB_INFO_TAG:
393            [self updateInfoGeneral];
394            break;
395        case TAB_ACTIVITY_TAG:
396            [self updateInfoActivity];
397            break;
398        case TAB_PEERS_TAG:
399            [self updateInfoPeers];
400            break;
401        case TAB_FILES_TAG:
402            [self updateInfoFiles];
403            break;
404        case TAB_OPTIONS_TAG:
405            [self updateInfoOptions];
406            break;
407    }
408}
409
410- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
411{
412    SEL action = [menuItem action];
413   
414    if (action == @selector(revealFile:))
415    {
416        if ([fTabMatrix selectedTag] != TAB_FILES_TAG)
417            return NO;
418       
419        NSString * downloadFolder = [[fTorrents objectAtIndex: 0] downloadFolder];
420        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
421        int i;
422        for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
423            if ([[NSFileManager defaultManager] fileExistsAtPath:
424                    [downloadFolder stringByAppendingPathComponent: [[fFiles objectAtIndex: i] objectForKey: @"Path"]]])
425                return YES;
426        return NO;
427    }
428   
429    if (action == @selector(setCheck:))
430    {
431        if ([fFileOutline numberOfSelectedRows] <= 0)
432            return NO;
433       
434        Torrent * torrent = [fTorrents objectAtIndex: 0];
435        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
436        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
437        int i, state = (menuItem == fFileCheckItem) ? NSOnState : NSOffState;
438        for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
439            [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
440       
441        return [torrent checkForFiles: itemIndexes] != state && [torrent canChangeDownloadCheckForFiles: itemIndexes];
442    }
443   
444    if (action == @selector(setOnlySelectedCheck:))
445    {
446        if ([fFileOutline numberOfSelectedRows] <= 0)
447            return NO;
448       
449        Torrent * torrent = [fTorrents objectAtIndex: 0];
450        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
451        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
452        int i;
453        for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
454            [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
455           
456        return [torrent canChangeDownloadCheckForFiles: itemIndexes];
457    }
458   
459    if (action == @selector(setPriority:))
460    {
461        if ([fFileOutline numberOfSelectedRows] <= 0)
462        {
463            [menuItem setState: NSOffState];
464            return NO;
465        }
466       
467        //determine which priorities are checked
468        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
469        BOOL current = NO, other = NO;
470        int i, priority;
471        Torrent * torrent = [fTorrents objectAtIndex: 0];
472       
473        if (menuItem == fFilePriorityHigh)
474            priority = TR_PRI_HIGH;
475        else if (menuItem == fFilePriorityLow)
476            priority = TR_PRI_LOW;
477        else
478            priority = TR_PRI_NORMAL;
479       
480        NSIndexSet * fileIndexSet;
481        for (i = [indexSet firstIndex]; i != NSNotFound && (!current || !other); i = [indexSet indexGreaterThanIndex: i])
482        {
483            fileIndexSet = [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"];
484            if (![torrent canChangeDownloadCheckForFiles: fileIndexSet])
485                continue;
486            else if ([torrent hasFilePriority: priority forIndexes: fileIndexSet])
487                current = YES;
488            else
489                other = YES;
490        }
491       
492        [menuItem setState: current ? NSOnState : NSOffState];
493        return current || other;
494    }
495   
496    return YES;
497}
498
499- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
500{
501    NSRect windowRect = [window frame];
502    windowRect.size.width = [window minSize].width;
503    return windowRect;
504}
505
506- (void) setTab: (id) sender
507{
508    [self updateInfoStats];
509   
510    BOOL oldCanResizeVertical = fCurrentTabTag == TAB_PEERS_TAG || fCurrentTabTag == TAB_FILES_TAG, canResizeVertical;
511    int oldTabTag = fCurrentTabTag;
512    fCurrentTabTag = [fTabMatrix selectedTag];
513   
514    NSView * view;
515    NSString * identifier, * title;;
516    switch (fCurrentTabTag)
517    {
518        case TAB_INFO_TAG:
519            view = fInfoView;
520            identifier = TAB_INFO_IDENT;
521            title = NSLocalizedString(@"General Info", "Inspector -> title");
522            canResizeVertical = NO;
523            break;
524        case TAB_ACTIVITY_TAG:
525            view = fActivityView;
526            identifier = TAB_ACTIVITY_IDENT;
527            title = NSLocalizedString(@"Activity", "Inspector -> title");
528            canResizeVertical = NO;
529           
530            [fPiecesView updateView: YES];
531            break;
532        case TAB_PEERS_TAG:
533            view = fPeersView;
534            identifier = TAB_PEERS_IDENT;
535            title = NSLocalizedString(@"Peers", "Inspector -> title");
536            canResizeVertical = YES;
537            break;
538        case TAB_FILES_TAG:
539            view = fFilesView;
540            identifier = TAB_FILES_IDENT;
541            title = NSLocalizedString(@"Files", "Inspector -> title");
542            canResizeVertical = YES;
543            break;
544        case TAB_OPTIONS_TAG:
545            view = fOptionsView;
546            identifier = TAB_OPTIONS_IDENT;
547            title = NSLocalizedString(@"Options", "Inspector -> title");
548            canResizeVertical = NO;
549            break;
550        default:
551            return;
552    }
553       
554    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
555   
556    NSWindow * window = [self window];
557   
558    float oldHeight = 0;
559    if (oldTabTag != INVALID)
560    {
561        if (fCurrentTabTag == oldTabTag)
562            return;
563       
564        //deselect old tab item
565        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
566       
567        //get old view
568        NSView * oldView = [self tabViewForTag: oldTabTag];
569        [oldView setHidden: YES];
570        [oldView removeFromSuperview];
571       
572        oldHeight = [oldView frame].size.height;
573       
574        //save old size
575        if (oldCanResizeVertical)
576            [[NSUserDefaults standardUserDefaults] setFloat: [oldView frame].size.height forKey: @"InspectorContentHeight"];
577    }
578   
579    [window setTitle: [NSString stringWithFormat: @"%@ - %@", title, NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]];
580   
581    //selected tab item
582    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
583   
584    NSRect windowRect = [window frame], viewRect = [view frame];
585   
586    if (canResizeVertical)
587    {
588        float height = [[NSUserDefaults standardUserDefaults] floatForKey: @"InspectorContentHeight"];
589        if (height != 0)
590            viewRect.size.height = MAX(height, TAB_MIN_HEIGHT);
591    }
592   
593    float difference = (viewRect.size.height - oldHeight) * [window userSpaceScaleFactor];
594    windowRect.origin.y -= difference;
595    windowRect.size.height += difference;
596   
597    if (canResizeVertical)
598    {
599        if (!oldCanResizeVertical)
600        {
601            [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height - viewRect.size.height + TAB_MIN_HEIGHT)];
602            [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
603        }
604    }
605    else
606    {
607        [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height)];
608        [window setMaxSize: NSMakeSize(FLT_MAX, windowRect.size.height)];
609    }
610   
611    viewRect.size.width = windowRect.size.width;
612    [view setFrame: viewRect];
613   
614    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
615    [[window contentView] addSubview: view];
616    [view setHidden: NO];
617}
618
619- (void) setNextTab
620{
621    int tag = [fTabMatrix selectedTag]+1;
622    if (tag >= [fTabMatrix numberOfColumns])
623        tag = 0;
624   
625    [fTabMatrix selectCellWithTag: tag];
626    [self setTab: nil];
627}
628
629- (void) setPreviousTab
630{
631    int tag = [fTabMatrix selectedTag]-1;
632    if (tag < 0)
633        tag = [fTabMatrix numberOfColumns]-1;
634   
635    [fTabMatrix selectCellWithTag: tag];
636    [self setTab: nil];
637}
638
639- (int) numberOfRowsInTableView: (NSTableView *) tableView
640{
641    if (tableView == fPeerTable)
642        return fPeers ? [fPeers count] : 0;
643    return 0;
644}
645
646- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
647{
648    if (tableView == fPeerTable)
649    {
650        NSString * ident = [column identifier];
651        NSDictionary * peer = [fPeers objectAtIndex: row];
652       
653        if ([ident isEqualToString: @"Encryption"])
654        {
655            if ([[peer objectForKey: @"Encryption"] boolValue])
656            {
657                if (!fLockImage)
658                    fLockImage = [NSImage imageNamed: @"Lock.tiff"];
659                return fLockImage;
660            }
661            else
662                return nil;
663        }
664        else if ([ident isEqualToString: @"Client"])
665            return [peer objectForKey: @"Client"];
666        else if  ([ident isEqualToString: @"Progress"])
667            return [peer objectForKey: @"Progress"];
668        else if ([ident isEqualToString: @"UL To"])
669        {
670            NSNumber * rate;
671            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
672        }
673        else if ([ident isEqualToString: @"DL From"])
674        {
675            NSNumber * rate;
676            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
677        }
678        else
679            return [peer objectForKey: @"IP"];
680    }
681    return nil;
682}
683
684- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
685{
686    if (tableView == fPeerTable)
687    {
688        if (fPeers)
689        {
690            NSArray * oldPeers = fPeers;
691            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
692            [oldPeers release];
693            [tableView reloadData];
694        }
695    }
696}
697
698- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) row
699{
700    return tableView != fPeerTable;
701}
702
703- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
704                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
705{
706    if (tableView == fPeerTable)
707    {
708        NSDictionary * peer = [fPeers objectAtIndex: row];
709        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
710       
711        [components addObject: [NSString stringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
712            "Inspector -> Peers tab -> table row tooltip"), [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
713       
714        if ([[peer objectForKey: @"Encryption"] boolValue])
715            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
716       
717        int port;
718        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
719            [components addObject: [NSString stringWithFormat:
720                                    NSLocalizedString(@"Port: %d", "Inspector -> Peers tab -> table row tooltip"), port]];
721        else
722            [components addObject: NSLocalizedString(@"Port: N/A", "Inspector -> Peers tab -> table row tooltip")];
723       
724        switch ([[peer objectForKey: @"From"] intValue])
725        {
726            case TR_PEER_FROM_TRACKER:
727                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
728                break;
729            case TR_PEER_FROM_INCOMING:
730                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
731                break;
732            case TR_PEER_FROM_CACHE:
733                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
734                break;
735            case TR_PEER_FROM_PEX:
736                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
737                break;
738        }
739       
740        return [components componentsJoinedByString: @"\n"];
741    }
742    return nil;
743}
744
745- (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
746{
747    if (!item)
748        return [fFiles count];
749    return [[item objectForKey: @"IsFolder"] boolValue] ? [[item objectForKey: @"Children"] count] : 0;
750}
751
752- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
753{
754    return [[item objectForKey: @"IsFolder"] boolValue];
755}
756
757- (id) outlineView: (NSOutlineView *) outlineView child: (int) index ofItem: (id) item
758{
759    return [(item ? [item objectForKey: @"Children"] : fFiles) objectAtIndex: index];
760}
761
762- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
763{
764    if ([[tableColumn identifier] isEqualToString: @"Check"])
765        return [NSNumber numberWithInt: [[fTorrents objectAtIndex: 0] checkForFiles: [item objectForKey: @"Indexes"]]];
766    else
767        return item;
768}
769
770- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
771            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
772{
773    NSString * identifier = [tableColumn identifier];
774    if ([identifier isEqualToString: @"Check"])
775        [cell setEnabled: [[fTorrents objectAtIndex: 0] canChangeDownloadCheckForFiles: [item objectForKey: @"Indexes"]]];
776    else if ([identifier isEqualToString: @"Priority"])
777        [cell setRepresentedObject: item];
778    else;
779}
780
781- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
782        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
783{
784    NSString * identifier = [tableColumn identifier];
785    if ([identifier isEqualToString: @"Check"])
786    {
787        Torrent * torrent = [fTorrents objectAtIndex: 0];
788        NSIndexSet * indexSet;
789        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
790            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
791        else
792            indexSet = [item objectForKey: @"Indexes"];
793       
794        [torrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
795        [fFileOutline reloadData];
796       
797        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
798    }
799}
800
801- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
802        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
803{
804    NSString * ident = [tableColumn identifier];
805    if ([ident isEqualToString: @"Name"])
806        return [[[fTorrents objectAtIndex: 0] downloadFolder] stringByAppendingPathComponent: [item objectForKey: @"Path"]];
807    else if ([ident isEqualToString: @"Check"])
808    {
809        switch ([cell state])
810        {
811            case NSOffState:
812                return NSLocalizedString(@"Don't Download", "Inspector -> files tab -> tooltip");
813            case NSOnState:
814                return NSLocalizedString(@"Download", "Inspector -> files tab -> tooltip");
815            case NSMixedState:
816                return NSLocalizedString(@"Download Some", "Inspector -> files tab -> tooltip");
817        }
818    }
819    else if ([ident isEqualToString: @"Priority"])
820    {
821        NSSet * priorities = [[fTorrents objectAtIndex: 0] filePrioritiesForIndexes: [item objectForKey: @"Indexes"]];
822        switch([priorities count])
823        {
824            case 0:
825                return NSLocalizedString(@"Priority Not Available", "Inspector -> files tab -> tooltip");
826            case 1:
827                switch ([[priorities anyObject] intValue])
828                {
829                    case TR_PRI_LOW:
830                        return NSLocalizedString(@"Low Priority", "Inspector -> files tab -> tooltip");
831                    case TR_PRI_HIGH:
832                        return NSLocalizedString(@"High Priority", "Inspector -> files tab -> tooltip");
833                    case TR_PRI_NORMAL:
834                        return NSLocalizedString(@"Normal Priority", "Inspector -> files tab -> tooltip");
835                }
836                break;
837            default:
838                return NSLocalizedString(@"Multiple Priorities", "Inspector -> files tab -> tooltip");
839        }
840    }
841    else;
842   
843    return nil;
844}
845
846- (float) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
847{
848    if ([[item objectForKey: @"IsFolder"] boolValue])
849        return FILE_ROW_SMALL_HEIGHT;
850    else
851        return [outlineView rowHeight];
852}
853
854- (void) mouseMoved: (NSEvent *) event
855{
856    [fFileOutline setHoverRowForEvent: fCurrentTabTag == TAB_FILES_TAG ? event : nil];
857}
858
859- (void) setPiecesView: (id) sender
860{
861    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
862}
863
864- (void) setPiecesViewForAvailable: (BOOL) available
865{
866    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
867    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
868   
869    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
870    [fPiecesView updateView: YES];
871}
872
873- (void) revealTorrentFile: (id) sender
874{
875    if ([fTorrents count] > 0)
876        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
877}
878
879- (void) revealDataFile: (id) sender
880{
881    if ([fTorrents count] > 0)
882        [[fTorrents objectAtIndex: 0] revealData];
883}
884
885- (void) revealFile: (id) sender
886{
887    if (!fFiles)
888        return;
889   
890    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
891    NSIndexSet * indexes = [fFileOutline selectedRowIndexes];
892    int i;
893    for (i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
894        [[NSWorkspace sharedWorkspace] selectFile: [folder stringByAppendingPathComponent:
895                [[fFileOutline itemAtRow: i] objectForKey: @"Path"]] inFileViewerRootedAtPath: nil];
896}
897
898- (void) setCheck: (id) sender
899{
900    int state = sender == fFileCheckItem ? NSOnState : NSOffState;
901   
902    Torrent * torrent = [fTorrents objectAtIndex: 0];
903    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
904    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
905    int i;
906    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
907        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
908   
909    [torrent setFileCheckState: state forIndexes: itemIndexes];
910    [fFileOutline reloadData];
911}
912
913- (void) setOnlySelectedCheck: (id) sender
914{
915    Torrent * torrent = [fTorrents objectAtIndex: 0];
916    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
917    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
918    int i;
919    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
920        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
921   
922    [torrent setFileCheckState: NSOnState forIndexes: itemIndexes];
923   
924    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
925    [remainingItemIndexes removeIndexes: itemIndexes];
926    [torrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
927   
928    [fFileOutline reloadData];
929}
930
931- (void) setPriority: (id) sender
932{
933    int priority;
934    if (sender == fFilePriorityHigh)
935        priority = TR_PRI_HIGH;
936    else if (sender == fFilePriorityLow)
937        priority = TR_PRI_LOW;
938    else
939        priority = TR_PRI_NORMAL;
940   
941    Torrent * torrent = [fTorrents objectAtIndex: 0];
942    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
943    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
944    int i;
945    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
946        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
947   
948    [torrent setFilePriority: priority forIndexes: itemIndexes];
949    [fFileOutline reloadData];
950}
951
952- (void) setSpeedMode: (id) sender
953{
954    BOOL upload = sender == fUploadLimitPopUp;
955    int mode;
956    switch ([sender indexOfSelectedItem])
957    {
958        case OPTION_POPUP_LIMIT:
959            mode = TR_SPEEDLIMIT_SINGLE;
960            break;
961        case OPTION_POPUP_NO_LIMIT:
962            mode = TR_SPEEDLIMIT_UNLIMITED;
963            break;
964        case OPTION_POPUP_GLOBAL:
965            mode = TR_SPEEDLIMIT_GLOBAL;
966            break;
967        default:
968            return;
969    }
970   
971    Torrent * torrent;
972    NSEnumerator * enumerator = [fTorrents objectEnumerator];
973    while ((torrent = [enumerator nextObject]))
974        [torrent setSpeedMode: mode upload: upload];
975   
976    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
977   
978    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
979    [field setHidden: !single];
980    if (single)
981    {
982        [field selectText: self];
983        [[self window] makeKeyAndOrderFront:self];
984    }
985   
986    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
987    [label setHidden: !single];
988}
989
990- (void) setSpeedLimit: (id) sender
991{
992    BOOL upload = sender == fUploadLimitField;
993   
994    Torrent * torrent;
995    NSEnumerator * enumerator = [fTorrents objectEnumerator];
996
997    int limit = [sender intValue];
998    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%i", limit]] || limit < 0)
999    {
1000        NSBeep();
1001       
1002        torrent = [enumerator nextObject]; //use first torrent
1003        limit = [torrent speedLimit: upload];
1004        while ((torrent = [enumerator nextObject]))
1005            if (limit != [torrent speedLimit: upload])
1006            {
1007                [sender setStringValue: @""];
1008                return;
1009            }
1010       
1011        [sender setIntValue: limit];
1012    }
1013    else
1014    {
1015        while ((torrent = [enumerator nextObject]))
1016            [torrent setSpeedLimit: limit upload: upload];
1017    }
1018}
1019
1020- (void) setRatioSetting: (id) sender
1021{
1022    int setting;
1023    switch ([sender indexOfSelectedItem])
1024    {
1025        case OPTION_POPUP_LIMIT:
1026            setting = NSOnState;
1027            break;
1028        case OPTION_POPUP_NO_LIMIT:
1029            setting = NSOffState;
1030            break;
1031        case OPTION_POPUP_GLOBAL:
1032            setting = NSMixedState;
1033            break;
1034        default:
1035            return;
1036    }
1037   
1038    Torrent * torrent;
1039    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1040    while ((torrent = [enumerator nextObject]))
1041        [torrent setRatioSetting: setting];
1042   
1043    BOOL single = setting == NSOnState;
1044    [fRatioLimitField setHidden: !single];
1045    if (single)
1046    {
1047        [fRatioLimitField selectText: self];
1048        [[self window] makeKeyAndOrderFront:self];
1049    }
1050   
1051    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1052}
1053
1054- (void) setRatioLimit: (id) sender
1055{
1056    Torrent * torrent;
1057    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1058
1059    float ratioLimit = [sender floatValue];
1060    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]] || ratioLimit < 0)
1061    {
1062        NSBeep();
1063        float ratioLimit = [[enumerator nextObject] ratioLimit]; //use first torrent
1064        while ((torrent = [enumerator nextObject]))
1065            if (ratioLimit != [torrent ratioLimit])
1066            {
1067                [sender setStringValue: @""];
1068                return;
1069            }
1070       
1071        [sender setFloatValue: ratioLimit];
1072    }
1073    else
1074    {
1075        while ((torrent = [enumerator nextObject]))
1076            [torrent setRatioLimit: ratioLimit];
1077    }
1078   
1079    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1080}
1081
1082- (void) setPex: (id) sender
1083{
1084        int state = [sender state];
1085        if (state == NSMixedState)
1086        {
1087                state = NSOnState;
1088                [sender setState: state];
1089        }
1090       
1091        Torrent * torrent;
1092    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1093       
1094        while ((torrent = [enumerator nextObject]))
1095                [torrent setPex: state == NSOnState];
1096}
1097
1098@end
1099
1100@implementation InfoWindowController (Private)
1101
1102- (void) updateInfoGeneral
1103{   
1104    if ([fTorrents count] != 1)
1105        return;
1106   
1107    Torrent * torrent = [fTorrents objectAtIndex: 0];
1108   
1109    NSString * tracker = [[torrent trackerAddress] stringByAppendingString: [torrent trackerAddressAnnounce]];
1110    [fTrackerField setStringValue: tracker];
1111    [fTrackerField setToolTip: tracker];
1112   
1113    NSString * location = [torrent dataLocation];
1114    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1115    [fDataLocationField setToolTip: location];
1116}
1117
1118- (void) updateInfoActivity
1119{
1120    int numberSelected = [fTorrents count];
1121    if (numberSelected == 0)
1122        return;
1123   
1124    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1125    Torrent * torrent;
1126    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1127    while ((torrent = [enumerator nextObject]))
1128    {
1129        have += [torrent haveTotal];
1130        haveVerified += [torrent haveVerified];
1131        downloadedTotal += [torrent downloadedTotal];
1132        uploadedTotal += [torrent uploadedTotal];
1133        failedHash += [torrent failedHash];
1134    }
1135   
1136    if (have == 0)
1137        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1138    else if (have == haveVerified)
1139        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ verified",
1140                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: haveVerified]]];
1141    else
1142        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ (%@ verified)",
1143                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: have], [NSString stringForFileSize: haveVerified]]];
1144   
1145    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1146    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1147    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1148   
1149    if (numberSelected == 1)
1150    {
1151        torrent = [fTorrents objectAtIndex: 0];
1152       
1153        [fStateField setStringValue: [torrent stateString]];
1154        [fProgressField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1155                    "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1156        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1157        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1158       
1159        NSString * errorMessage = [torrent errorMessage];
1160        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1161        {
1162            [fErrorMessageView setString: errorMessage];
1163            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1164        }
1165       
1166        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1167        [fDateActivityField setObjectValue: [torrent dateActivity]];
1168       
1169        [fPiecesView updateView: NO];
1170    }
1171}
1172
1173- (void) updateInfoPeers
1174{
1175    if ([fTorrents count] != 1)
1176        return;
1177    Torrent * torrent = [fTorrents objectAtIndex: 0];
1178   
1179    int seeders = [torrent seeders], leechers = [torrent leechers], completed = [torrent completedFromTracker];
1180    [fSeedersField setStringValue: seeders >= 0 ? [NSString stringWithFormat: @"%d", seeders] : @""];
1181    [fLeechersField setStringValue: leechers >= 0 ? [NSString stringWithFormat: @"%d", leechers] : @""];
1182    [fCompletedFromTrackerField setStringValue: completed >= 0 ? [NSString stringWithFormat: @"%d", completed] : @""];
1183   
1184    BOOL active = [torrent isActive];
1185   
1186    if (active)
1187    {
1188        int total = [torrent totalPeersConnected];
1189        NSString * connected = [NSString stringWithFormat:
1190                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1191       
1192        if (total > 0)
1193        {
1194            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
1195            int count;
1196            if ((count = [torrent totalPeersTracker]) > 0)
1197                [components addObject: [NSString stringWithFormat:
1198                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1199            if ((count = [torrent totalPeersIncoming]) > 0)
1200                [components addObject: [NSString stringWithFormat:
1201                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1202            if ((count = [torrent totalPeersPex]) > 0)
1203                [components addObject: [NSString stringWithFormat:
1204                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1205            if ((count = [torrent totalPeersCache]) > 0)
1206                [components addObject: [NSString stringWithFormat:
1207                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1208           
1209            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1210        }
1211       
1212        [fConnectedPeersField setStringValue: connected];
1213       
1214        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1215        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1216    }
1217    else
1218    {
1219        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
1220        [fDownloadingFromField setStringValue: @""];
1221        [fUploadingToField setStringValue: @""];
1222    }
1223   
1224    [fKnownField setIntValue: [torrent totalPeersKnown]];
1225   
1226    [fPeers release];
1227    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1228   
1229    [fPeerTable reloadData];
1230}
1231
1232- (void) updateInfoFiles
1233{
1234    if ([fTorrents count] == 1)
1235    {
1236        [[fTorrents objectAtIndex: 0] updateFileStat];
1237        [fFileOutline reloadData];
1238    }
1239}
1240
1241- (void) updateInfoOptions
1242{
1243    if ([fTorrents count] == 0)
1244        return;
1245
1246    //get bandwidth info
1247    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1248    Torrent * torrent = [enumerator nextObject]; //first torrent
1249   
1250    int uploadSpeedMode = [torrent speedMode: YES],
1251        uploadSpeedLimit = [torrent speedLimit: YES],
1252        downloadSpeedMode = [torrent speedMode: NO],
1253        downloadSpeedLimit = [torrent speedLimit: NO];
1254   
1255    while ((torrent = [enumerator nextObject])
1256            && (uploadSpeedMode != INVALID || uploadSpeedLimit != INVALID
1257                || downloadSpeedMode != INVALID || downloadSpeedLimit != INVALID))
1258    {
1259        if (uploadSpeedMode != INVALID && uploadSpeedMode != [torrent speedMode: YES])
1260            uploadSpeedMode = INVALID;
1261       
1262        if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
1263            uploadSpeedLimit = INVALID;
1264       
1265        if (downloadSpeedMode != INVALID && downloadSpeedMode != [torrent speedMode: NO])
1266            downloadSpeedMode = INVALID;
1267       
1268        if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
1269            downloadSpeedLimit = INVALID;
1270    }
1271   
1272    //set upload view
1273    int index;
1274    if (uploadSpeedMode == TR_SPEEDLIMIT_SINGLE)
1275        index = OPTION_POPUP_LIMIT;
1276    else if (uploadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
1277        index = OPTION_POPUP_NO_LIMIT;
1278    else if (uploadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
1279        index = OPTION_POPUP_GLOBAL;
1280    else
1281        index = -1;
1282    [fUploadLimitPopUp selectItemAtIndex: index];
1283    [fUploadLimitPopUp setEnabled: YES];
1284   
1285    [fUploadLimitLabel setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1286    [fUploadLimitField setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1287    if (uploadSpeedLimit != INVALID)
1288        [fUploadLimitField setIntValue: uploadSpeedLimit];
1289    else
1290        [fUploadLimitField setStringValue: @""];
1291   
1292    //set download view
1293    if (downloadSpeedMode == TR_SPEEDLIMIT_SINGLE)
1294        index = OPTION_POPUP_LIMIT;
1295    else if (downloadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
1296        index = OPTION_POPUP_NO_LIMIT;
1297    else if (downloadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
1298        index = OPTION_POPUP_GLOBAL;
1299    else
1300        index = -1;
1301    [fDownloadLimitPopUp selectItemAtIndex: index];
1302    [fDownloadLimitPopUp setEnabled: YES];
1303   
1304    [fDownloadLimitLabel setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1305    [fDownloadLimitField setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1306    if (downloadSpeedLimit != INVALID)
1307        [fDownloadLimitField setIntValue: downloadSpeedLimit];
1308    else
1309        [fDownloadLimitField setStringValue: @""];
1310   
1311    //get ratio info
1312    enumerator = [fTorrents objectEnumerator];
1313    torrent = [enumerator nextObject]; //first torrent
1314   
1315    int checkRatio = [torrent ratioSetting];
1316    float ratioLimit = [torrent ratioLimit];
1317   
1318    while ((torrent = [enumerator nextObject]) && (checkRatio != INVALID || checkRatio != INVALID))
1319    {
1320        if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
1321            checkRatio = INVALID;
1322       
1323        if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
1324            ratioLimit = INVALID;
1325    }
1326   
1327    //set ratio view
1328    if (checkRatio == NSOnState)
1329        index = OPTION_POPUP_LIMIT;
1330    else if (checkRatio == NSOffState)
1331        index = OPTION_POPUP_NO_LIMIT;
1332    else if (checkRatio == NSMixedState)
1333        index = OPTION_POPUP_GLOBAL;
1334    else
1335        index = -1;
1336    [fRatioPopUp selectItemAtIndex: index];
1337    [fRatioPopUp setEnabled: YES];
1338   
1339    [fRatioLimitField setHidden: checkRatio != NSOnState];
1340    if (ratioLimit != INVALID)
1341        [fRatioLimitField setFloatValue: ratioLimit];
1342    else
1343        [fRatioLimitField setStringValue: @""];
1344   
1345    //set pex check
1346    enumerator = [fTorrents objectEnumerator];
1347    torrent = [enumerator nextObject]; //first torrent
1348   
1349    BOOL pexEnabled = ![torrent privateTorrent] && [torrent isPaused];
1350    int pexState = [torrent pex] ? NSOnState : NSOffState;
1351   
1352    while ((torrent = [enumerator nextObject]) && (pexEnabled || pexState != NSMixedState))
1353    {
1354        if (pexEnabled)
1355            pexEnabled = ![torrent privateTorrent] && [torrent isPaused];
1356       
1357        if (pexState != NSMixedState && pexState != ([torrent pex] ? NSOnState : NSOffState))
1358            pexState = NSMixedState;
1359    }
1360   
1361    [fPexCheck setEnabled: pexEnabled];
1362    [fPexCheck setState: pexState];
1363    [fPexCheck setToolTip: !pexEnabled ? NSLocalizedString(@"PEX can only be toggled on paused public torrents.",
1364                                "Inspector -> pex check") : nil];
1365}
1366
1367- (NSView *) tabViewForTag: (int) tag
1368{
1369    switch (tag)
1370    {
1371        case TAB_INFO_TAG:
1372            return fInfoView;
1373        case TAB_ACTIVITY_TAG:
1374            return fActivityView;
1375        case TAB_PEERS_TAG:
1376            return fPeersView;
1377        case TAB_FILES_TAG:
1378            return fFilesView;
1379        case TAB_OPTIONS_TAG:
1380            return fOptionsView;
1381        default:
1382            return nil;
1383    }
1384}
1385
1386- (NSArray *) peerSortDescriptors
1387{
1388    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1389   
1390    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1391    BOOL useSecond = YES, asc = YES;
1392    if ([oldDescriptors count] > 0)
1393    {
1394        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1395        [descriptors addObject: descriptor];
1396       
1397        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1398            asc = [descriptor ascending];
1399    }
1400   
1401    //sort by IP after primary sort
1402    if (useSecond)
1403    {
1404        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1405                                                                        selector: @selector(compareIP:)];
1406        [descriptors addObject: secondDescriptor];
1407        [secondDescriptor release];
1408    }
1409   
1410    return descriptors;
1411}
1412
1413@end
Note: See TracBrowser for help on using the repository browser.