source: trunk/macosx/InfoWindowController.m @ 3325

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

ensure everything is deallocated properly on quit, libtransmission is closed properly, and inspector window height is remembered

  • Property svn:keywords set to Date Rev Author Id
File size: 50.2 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 3325 2007-10-08 04:15:25Z 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;
516    switch (fCurrentTabTag)
517    {
518        case TAB_INFO_TAG:
519            view = fInfoView;
520            identifier = TAB_INFO_IDENT;
521            canResizeVertical = NO;
522            break;
523        case TAB_ACTIVITY_TAG:
524            view = fActivityView;
525            identifier = TAB_ACTIVITY_IDENT;
526            canResizeVertical = NO;
527           
528            [fPiecesView updateView: YES];
529            break;
530        case TAB_PEERS_TAG:
531            view = fPeersView;
532            identifier = TAB_PEERS_IDENT;
533            canResizeVertical = YES;
534            break;
535        case TAB_FILES_TAG:
536            view = fFilesView;
537            identifier = TAB_FILES_IDENT;
538            canResizeVertical = YES;
539            break;
540        case TAB_OPTIONS_TAG:
541            view = fOptionsView;
542            identifier = TAB_OPTIONS_IDENT;
543            canResizeVertical = NO;
544            break;
545        default:
546            return;
547    }
548       
549    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
550   
551    NSWindow * window = [self window];
552   
553    float oldHeight = 0;
554    if (oldTabTag != INVALID)
555    {
556        if (fCurrentTabTag == oldTabTag)
557            return;
558       
559        //deselect old tab item
560        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
561       
562        //get old view
563        NSView * oldView = [self tabViewForTag: oldTabTag];
564        [oldView setHidden: YES];
565        [oldView removeFromSuperview];
566       
567        oldHeight = [oldView frame].size.height;
568       
569        //save old size
570        if (oldCanResizeVertical)
571            [[NSUserDefaults standardUserDefaults] setFloat: [oldView frame].size.height forKey: @"InspectorContentHeight"];
572    }
573   
574    //selected tab item
575    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
576   
577    NSRect windowRect = [window frame], viewRect = [view frame];
578   
579    if (canResizeVertical)
580    {
581        float height = [[NSUserDefaults standardUserDefaults] floatForKey: @"InspectorContentHeight"];
582        if (height != 0)
583            viewRect.size.height = MAX(height, TAB_MIN_HEIGHT);
584    }
585   
586    float difference = (viewRect.size.height - oldHeight) * [window userSpaceScaleFactor];
587    windowRect.origin.y -= difference;
588    windowRect.size.height += difference;
589   
590    if (canResizeVertical)
591    {
592        if (!oldCanResizeVertical)
593        {
594            [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height - viewRect.size.height + TAB_MIN_HEIGHT)];
595            [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
596        }
597    }
598    else
599    {
600        [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height)];
601        [window setMaxSize: NSMakeSize(FLT_MAX, windowRect.size.height)];
602    }
603   
604    viewRect.size.width = windowRect.size.width;
605    [view setFrame: viewRect];
606   
607    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
608    [[window contentView] addSubview: view];
609    [view setHidden: NO];
610}
611
612- (void) setNextTab
613{
614    int tag = [fTabMatrix selectedTag]+1;
615    if (tag >= [fTabMatrix numberOfColumns])
616        tag = 0;
617   
618    [fTabMatrix selectCellWithTag: tag];
619    [self setTab: nil];
620}
621
622- (void) setPreviousTab
623{
624    int tag = [fTabMatrix selectedTag]-1;
625    if (tag < 0)
626        tag = [fTabMatrix numberOfColumns]-1;
627   
628    [fTabMatrix selectCellWithTag: tag];
629    [self setTab: nil];
630}
631
632- (int) numberOfRowsInTableView: (NSTableView *) tableView
633{
634    if (tableView == fPeerTable)
635        return fPeers ? [fPeers count] : 0;
636    return 0;
637}
638
639- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
640{
641    if (tableView == fPeerTable)
642    {
643        NSString * ident = [column identifier];
644        NSDictionary * peer = [fPeers objectAtIndex: row];
645       
646        if ([ident isEqualToString: @"Encryption"])
647        {
648            if ([[peer objectForKey: @"Encryption"] boolValue])
649            {
650                if (!fLockImage)
651                    fLockImage = [NSImage imageNamed: @"Lock.tiff"];
652                return fLockImage;
653            }
654            else
655                return nil;
656        }
657        else if ([ident isEqualToString: @"Client"])
658            return [peer objectForKey: @"Client"];
659        else if  ([ident isEqualToString: @"Progress"])
660            return [peer objectForKey: @"Progress"];
661        else if ([ident isEqualToString: @"UL To"])
662        {
663            NSNumber * rate;
664            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
665        }
666        else if ([ident isEqualToString: @"DL From"])
667        {
668            NSNumber * rate;
669            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
670        }
671        else
672            return [peer objectForKey: @"IP"];
673    }
674    return nil;
675}
676
677- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
678{
679    if (tableView == fPeerTable)
680    {
681        if (fPeers)
682        {
683            NSArray * oldPeers = fPeers;
684            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
685            [oldPeers release];
686            [tableView reloadData];
687        }
688    }
689}
690
691- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) row
692{
693    return tableView != fPeerTable;
694}
695
696- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
697                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
698{
699    if (tableView == fPeerTable)
700    {
701        NSDictionary * peer = [fPeers objectAtIndex: row];
702        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
703       
704        [components addObject: [NSString stringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
705            "Inspector -> Peers tab -> table row tooltip"), [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
706       
707        if ([[peer objectForKey: @"Encryption"] boolValue])
708            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
709       
710        int port;
711        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
712            [components addObject: [NSString stringWithFormat:
713                                    NSLocalizedString(@"Port: %d", "Inspector -> Peers tab -> table row tooltip"), port]];
714        else
715            [components addObject: NSLocalizedString(@"Port: N/A", "Inspector -> Peers tab -> table row tooltip")];
716       
717        switch ([[peer objectForKey: @"From"] intValue])
718        {
719            case TR_PEER_FROM_TRACKER:
720                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
721                break;
722            case TR_PEER_FROM_INCOMING:
723                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
724                break;
725            case TR_PEER_FROM_CACHE:
726                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
727                break;
728            case TR_PEER_FROM_PEX:
729                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
730                break;
731        }
732       
733        return [components componentsJoinedByString: @"\n"];
734    }
735    return nil;
736}
737
738- (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
739{
740    if (!item)
741        return [fFiles count];
742    return [[item objectForKey: @"IsFolder"] boolValue] ? [[item objectForKey: @"Children"] count] : 0;
743}
744
745- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
746{
747    return [[item objectForKey: @"IsFolder"] boolValue];
748}
749
750- (id) outlineView: (NSOutlineView *) outlineView child: (int) index ofItem: (id) item
751{
752    return [(item ? [item objectForKey: @"Children"] : fFiles) objectAtIndex: index];
753}
754
755- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
756{
757    if ([[tableColumn identifier] isEqualToString: @"Check"])
758        return [NSNumber numberWithInt: [[fTorrents objectAtIndex: 0] checkForFiles: [item objectForKey: @"Indexes"]]];
759    else
760        return item;
761}
762
763- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
764            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
765{
766    NSString * identifier = [tableColumn identifier];
767    if ([identifier isEqualToString: @"Check"])
768        [cell setEnabled: [[fTorrents objectAtIndex: 0] canChangeDownloadCheckForFiles: [item objectForKey: @"Indexes"]]];
769    else if ([identifier isEqualToString: @"Priority"])
770        [cell setRepresentedObject: item];
771    else;
772}
773
774- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
775        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
776{
777    NSString * identifier = [tableColumn identifier];
778    if ([identifier isEqualToString: @"Check"])
779    {
780        Torrent * torrent = [fTorrents objectAtIndex: 0];
781        NSIndexSet * indexSet;
782        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
783            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
784        else
785            indexSet = [item objectForKey: @"Indexes"];
786       
787        [torrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
788        [fFileOutline reloadData];
789       
790        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
791    }
792}
793
794- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
795        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
796{
797    NSString * ident = [tableColumn identifier];
798    if ([ident isEqualToString: @"Name"])
799        return [[[fTorrents objectAtIndex: 0] downloadFolder] stringByAppendingPathComponent: [item objectForKey: @"Path"]];
800    else if ([ident isEqualToString: @"Check"])
801    {
802        int check = [cell state];
803        if (check == NSOffState)
804            return NSLocalizedString(@"Don't Download", "Inspector -> files tab -> tooltip");
805        else if (check == NSMixedState)
806            return NSLocalizedString(@"Download Some", "Inspector -> files tab -> tooltip");
807        else
808            return NSLocalizedString(@"Download", "Inspector -> files tab -> tooltip");
809    }
810    else if ([ident isEqualToString: @"Priority"])
811    {
812        NSSet * priorities = [[fTorrents objectAtIndex: 0] filePrioritiesForIndexes: [item objectForKey: @"Indexes"]];
813       
814        int count = [priorities count];
815        if (count == 0)
816            return NSLocalizedString(@"Priority Not Available", "Inspector -> files tab -> tooltip");
817        else if (count > 1)
818            return NSLocalizedString(@"Multiple Priorities", "Inspector -> files tab -> tooltip");
819        else
820        {
821            int priority = [[priorities anyObject] intValue];
822            if (priority == TR_PRI_LOW)
823                return NSLocalizedString(@"Low Priority", "Inspector -> files tab -> tooltip");
824            else if (priority == TR_PRI_HIGH)
825                return NSLocalizedString(@"High Priority", "Inspector -> files tab -> tooltip");
826            else
827                return NSLocalizedString(@"Normal Priority", "Inspector -> files tab -> tooltip");
828        }
829    }
830    else
831        return nil;
832}
833
834- (float) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
835{
836    if ([[item objectForKey: @"IsFolder"] boolValue])
837        return FILE_ROW_SMALL_HEIGHT;
838    else
839        return [outlineView rowHeight];
840}
841
842- (void) mouseMoved: (NSEvent *) event
843{
844    [fFileOutline setHoverRowForEvent: fCurrentTabTag == TAB_FILES_TAG ? event : nil];
845}
846
847- (void) setPiecesView: (id) sender
848{
849    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
850}
851
852- (void) setPiecesViewForAvailable: (BOOL) available
853{
854    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
855    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
856   
857    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
858    [fPiecesView updateView: YES];
859}
860
861- (void) revealTorrentFile: (id) sender
862{
863    if ([fTorrents count] > 0)
864        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
865}
866
867- (void) revealDataFile: (id) sender
868{
869    if ([fTorrents count] > 0)
870        [[fTorrents objectAtIndex: 0] revealData];
871}
872
873- (void) revealFile: (id) sender
874{
875    if (!fFiles)
876        return;
877   
878    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
879    NSIndexSet * indexes = [fFileOutline selectedRowIndexes];
880    int i;
881    for (i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
882        [[NSWorkspace sharedWorkspace] selectFile: [folder stringByAppendingPathComponent:
883                [[fFileOutline itemAtRow: i] objectForKey: @"Path"]] inFileViewerRootedAtPath: nil];
884}
885
886- (void) setCheck: (id) sender
887{
888    int state = sender == fFileCheckItem ? NSOnState : NSOffState;
889   
890    Torrent * torrent = [fTorrents objectAtIndex: 0];
891    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
892    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
893    int i;
894    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
895        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
896   
897    [torrent setFileCheckState: state forIndexes: itemIndexes];
898    [fFileOutline reloadData];
899}
900
901- (void) setOnlySelectedCheck: (id) sender
902{
903    Torrent * torrent = [fTorrents objectAtIndex: 0];
904    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
905    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
906    int i;
907    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
908        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
909   
910    [torrent setFileCheckState: NSOnState forIndexes: itemIndexes];
911   
912    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
913    [remainingItemIndexes removeIndexes: itemIndexes];
914    [torrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
915   
916    [fFileOutline reloadData];
917}
918
919- (void) setPriority: (id) sender
920{
921    int priority;
922    if (sender == fFilePriorityHigh)
923        priority = TR_PRI_HIGH;
924    else if (sender == fFilePriorityLow)
925        priority = TR_PRI_LOW;
926    else
927        priority = TR_PRI_NORMAL;
928   
929    Torrent * torrent = [fTorrents objectAtIndex: 0];
930    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
931    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
932    int i;
933    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
934        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
935   
936    [torrent setFilePriority: priority forIndexes: itemIndexes];
937    [fFileOutline reloadData];
938}
939
940- (void) setSpeedMode: (id) sender
941{
942    BOOL upload = sender == fUploadLimitPopUp;
943    int index = [sender indexOfSelectedItem], mode;
944    if (index == OPTION_POPUP_LIMIT)
945        mode = TR_SPEEDLIMIT_SINGLE;
946    else if (index == OPTION_POPUP_NO_LIMIT)
947        mode = TR_SPEEDLIMIT_UNLIMITED;
948    else
949        mode = TR_SPEEDLIMIT_GLOBAL;
950   
951    Torrent * torrent;
952    NSEnumerator * enumerator = [fTorrents objectEnumerator];
953    while ((torrent = [enumerator nextObject]))
954        [torrent setSpeedMode: mode upload: upload];
955   
956    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
957   
958    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
959    [field setHidden: !single];
960    if (single)
961    {
962        [field selectText: self];
963        [[self window] makeKeyAndOrderFront:self];
964    }
965   
966    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
967    [label setHidden: !single];
968}
969
970- (void) setSpeedLimit: (id) sender
971{
972    BOOL upload = sender == fUploadLimitField;
973   
974    Torrent * torrent;
975    NSEnumerator * enumerator = [fTorrents objectEnumerator];
976
977    int limit = [sender intValue];
978    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%i", limit]] || limit < 0)
979    {
980        NSBeep();
981       
982        torrent = [enumerator nextObject]; //use first torrent
983        limit = [torrent speedLimit: upload];
984        while ((torrent = [enumerator nextObject]))
985            if (limit != [torrent speedLimit: upload])
986            {
987                [sender setStringValue: @""];
988                return;
989            }
990       
991        [sender setIntValue: limit];
992    }
993    else
994    {
995        while ((torrent = [enumerator nextObject]))
996            [torrent setSpeedLimit: limit upload: upload];
997    }
998}
999
1000- (void) setRatioSetting: (id) sender
1001{
1002    int index = [sender indexOfSelectedItem], setting;
1003    if (index == OPTION_POPUP_LIMIT)
1004        setting = NSOnState;
1005    else if (index == OPTION_POPUP_NO_LIMIT)
1006        setting = NSOffState;
1007    else
1008        setting = NSMixedState;
1009   
1010    Torrent * torrent;
1011    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1012    while ((torrent = [enumerator nextObject]))
1013        [torrent setRatioSetting: setting];
1014   
1015    BOOL single = setting == NSOnState;
1016    [fRatioLimitField setHidden: !single];
1017    if (single)
1018    {
1019        [fRatioLimitField selectText: self];
1020        [[self window] makeKeyAndOrderFront:self];
1021    }
1022   
1023    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1024}
1025
1026- (void) setRatioLimit: (id) sender
1027{
1028    Torrent * torrent;
1029    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1030
1031    float ratioLimit = [sender floatValue];
1032    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]] || ratioLimit < 0)
1033    {
1034        NSBeep();
1035        float ratioLimit = [[enumerator nextObject] ratioLimit]; //use first torrent
1036        while ((torrent = [enumerator nextObject]))
1037            if (ratioLimit != [torrent ratioLimit])
1038            {
1039                [sender setStringValue: @""];
1040                return;
1041            }
1042       
1043        [sender setFloatValue: ratioLimit];
1044    }
1045    else
1046    {
1047        while ((torrent = [enumerator nextObject]))
1048            [torrent setRatioLimit: ratioLimit];
1049    }
1050   
1051    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1052}
1053
1054- (void) setPex: (id) sender
1055{
1056        int state = [sender state];
1057        if (state == NSMixedState)
1058        {
1059                state = NSOnState;
1060                [sender setState: state];
1061        }
1062       
1063        Torrent * torrent;
1064    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1065       
1066        while ((torrent = [enumerator nextObject]))
1067                [torrent setPex: state == NSOnState];
1068}
1069
1070@end
1071
1072@implementation InfoWindowController (Private)
1073
1074- (void) updateInfoGeneral
1075{   
1076    if ([fTorrents count] != 1)
1077        return;
1078   
1079    Torrent * torrent = [fTorrents objectAtIndex: 0];
1080   
1081    NSString * tracker = [[torrent trackerAddress] stringByAppendingString: [torrent trackerAddressAnnounce]];
1082    [fTrackerField setStringValue: tracker];
1083    [fTrackerField setToolTip: tracker];
1084   
1085    NSString * location = [torrent dataLocation];
1086    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1087    [fDataLocationField setToolTip: location];
1088}
1089
1090- (void) updateInfoActivity
1091{
1092    int numberSelected = [fTorrents count];
1093    if (numberSelected == 0)
1094        return;
1095   
1096    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1097    Torrent * torrent;
1098    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1099    while ((torrent = [enumerator nextObject]))
1100    {
1101        have += [torrent haveTotal];
1102        haveVerified += [torrent haveVerified];
1103        downloadedTotal += [torrent downloadedTotal];
1104        uploadedTotal += [torrent uploadedTotal];
1105        failedHash += [torrent failedHash];
1106    }
1107   
1108    if (have == 0)
1109        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1110    else if (have == haveVerified)
1111        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ verified",
1112                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: haveVerified]]];
1113    else
1114        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ (%@ verified)",
1115                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: have], [NSString stringForFileSize: haveVerified]]];
1116   
1117    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1118    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1119    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1120   
1121    if (numberSelected == 1)
1122    {
1123        torrent = [fTorrents objectAtIndex: 0];
1124       
1125        [fStateField setStringValue: [torrent stateString]];
1126        [fProgressField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1127                    "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1128        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1129        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1130       
1131        NSString * errorMessage = [torrent errorMessage];
1132        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1133        {
1134            [fErrorMessageView setString: errorMessage];
1135            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1136        }
1137       
1138        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1139        [fDateActivityField setObjectValue: [torrent dateActivity]];
1140       
1141        [fPiecesView updateView: NO];
1142    }
1143}
1144
1145- (void) updateInfoPeers
1146{
1147    if ([fTorrents count] != 1)
1148        return;
1149    Torrent * torrent = [fTorrents objectAtIndex: 0];
1150   
1151    int seeders = [torrent seeders], leechers = [torrent leechers], completed = [torrent completedFromTracker];
1152    [fSeedersField setStringValue: seeders > 0 ? [NSString stringWithFormat: @"%d", seeders] : @""];
1153    [fLeechersField setStringValue: leechers > 0 ? [NSString stringWithFormat: @"%d", leechers] : @""];
1154    [fCompletedFromTrackerField setStringValue: completed > 0 ? [NSString stringWithFormat: @"%d", completed] : @""];
1155   
1156    BOOL active = [torrent isActive];
1157   
1158    if (active)
1159    {
1160        int total = [torrent totalPeersConnected];
1161        NSString * connected = [NSString stringWithFormat:
1162                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1163       
1164        if (total > 0)
1165        {
1166            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
1167            int count;
1168            if ((count = [torrent totalPeersTracker]) > 0)
1169                [components addObject: [NSString stringWithFormat:
1170                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1171            if ((count = [torrent totalPeersIncoming]) > 0)
1172                [components addObject: [NSString stringWithFormat:
1173                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1174            if ((count = [torrent totalPeersPex]) > 0)
1175                [components addObject: [NSString stringWithFormat:
1176                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1177            if ((count = [torrent totalPeersCache]) > 0)
1178                [components addObject: [NSString stringWithFormat:
1179                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1180           
1181            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1182        }
1183       
1184        [fConnectedPeersField setStringValue: connected];
1185       
1186        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1187        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1188    }
1189    else
1190    {
1191        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
1192        [fDownloadingFromField setStringValue: @""];
1193        [fUploadingToField setStringValue: @""];
1194    }
1195   
1196    [fKnownField setIntValue: [torrent totalPeersKnown]];
1197   
1198    [fPeers release];
1199    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1200   
1201    [fPeerTable reloadData];
1202}
1203
1204- (void) updateInfoFiles
1205{
1206    if ([fTorrents count] == 1)
1207    {
1208        [[fTorrents objectAtIndex: 0] updateFileStat];
1209        [fFileOutline reloadData];
1210    }
1211}
1212
1213- (void) updateInfoOptions
1214{
1215    if ([fTorrents count] == 0)
1216        return;
1217
1218    //get bandwidth info
1219    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1220    Torrent * torrent = [enumerator nextObject]; //first torrent
1221   
1222    int uploadSpeedMode = [torrent speedMode: YES],
1223        uploadSpeedLimit = [torrent speedLimit: YES],
1224        downloadSpeedMode = [torrent speedMode: NO],
1225        downloadSpeedLimit = [torrent speedLimit: NO];
1226   
1227    while ((torrent = [enumerator nextObject])
1228            && (uploadSpeedMode != INVALID || uploadSpeedLimit != INVALID
1229                || downloadSpeedMode != INVALID || downloadSpeedLimit != INVALID))
1230    {
1231        if (uploadSpeedMode != INVALID && uploadSpeedMode != [torrent speedMode: YES])
1232            uploadSpeedMode = INVALID;
1233       
1234        if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
1235            uploadSpeedLimit = INVALID;
1236       
1237        if (downloadSpeedMode != INVALID && downloadSpeedMode != [torrent speedMode: NO])
1238            downloadSpeedMode = INVALID;
1239       
1240        if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
1241            downloadSpeedLimit = INVALID;
1242    }
1243   
1244    //set upload view
1245    int index;
1246    if (uploadSpeedMode == TR_SPEEDLIMIT_SINGLE)
1247        index = OPTION_POPUP_LIMIT;
1248    else if (uploadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
1249        index = OPTION_POPUP_NO_LIMIT;
1250    else if (uploadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
1251        index = OPTION_POPUP_GLOBAL;
1252    else
1253        index = -1;
1254    [fUploadLimitPopUp selectItemAtIndex: index];
1255    [fUploadLimitPopUp setEnabled: YES];
1256   
1257    [fUploadLimitLabel setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1258    [fUploadLimitField setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1259    if (uploadSpeedLimit != INVALID)
1260        [fUploadLimitField setIntValue: uploadSpeedLimit];
1261    else
1262        [fUploadLimitField setStringValue: @""];
1263   
1264    //set download view
1265    if (downloadSpeedMode == TR_SPEEDLIMIT_SINGLE)
1266        index = OPTION_POPUP_LIMIT;
1267    else if (downloadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
1268        index = OPTION_POPUP_NO_LIMIT;
1269    else if (downloadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
1270        index = OPTION_POPUP_GLOBAL;
1271    else
1272        index = -1;
1273    [fDownloadLimitPopUp selectItemAtIndex: index];
1274    [fDownloadLimitPopUp setEnabled: YES];
1275   
1276    [fDownloadLimitLabel setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1277    [fDownloadLimitField setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
1278    if (downloadSpeedLimit != INVALID)
1279        [fDownloadLimitField setIntValue: downloadSpeedLimit];
1280    else
1281        [fDownloadLimitField setStringValue: @""];
1282   
1283    //get ratio info
1284    enumerator = [fTorrents objectEnumerator];
1285    torrent = [enumerator nextObject]; //first torrent
1286   
1287    int checkRatio = [torrent ratioSetting];
1288    float ratioLimit = [torrent ratioLimit];
1289   
1290    while ((torrent = [enumerator nextObject]) && (checkRatio != INVALID || checkRatio != INVALID))
1291    {
1292        if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
1293            checkRatio = INVALID;
1294       
1295        if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
1296            ratioLimit = INVALID;
1297    }
1298   
1299    //set ratio view
1300    if (checkRatio == NSOnState)
1301        index = OPTION_POPUP_LIMIT;
1302    else if (checkRatio == NSOffState)
1303        index = OPTION_POPUP_NO_LIMIT;
1304    else if (checkRatio == NSMixedState)
1305        index = OPTION_POPUP_GLOBAL;
1306    else
1307        index = -1;
1308    [fRatioPopUp selectItemAtIndex: index];
1309    [fRatioPopUp setEnabled: YES];
1310   
1311    [fRatioLimitField setHidden: checkRatio != NSOnState];
1312    if (ratioLimit != INVALID)
1313        [fRatioLimitField setFloatValue: ratioLimit];
1314    else
1315        [fRatioLimitField setStringValue: @""];
1316   
1317    //set pex check
1318    enumerator = [fTorrents objectEnumerator];
1319    torrent = [enumerator nextObject]; //first torrent
1320   
1321    BOOL pexEnabled = ![torrent privateTorrent] && [torrent isPaused];
1322    int pexState = [torrent pex] ? NSOnState : NSOffState;
1323   
1324    while ((torrent = [enumerator nextObject]) && (pexEnabled || pexState != NSMixedState))
1325    {
1326        if (pexEnabled)
1327            pexEnabled = ![torrent privateTorrent] && [torrent isPaused];
1328       
1329        if (pexState != NSMixedState && pexState != ([torrent pex] ? NSOnState : NSOffState))
1330            pexState = NSMixedState;
1331    }
1332   
1333    [fPexCheck setEnabled: pexEnabled];
1334    [fPexCheck setState: pexState];
1335    [fPexCheck setToolTip: !pexEnabled ? NSLocalizedString(@"PEX can only be toggled on paused public torrents.",
1336                                "Inspector -> pex check") : nil];
1337}
1338
1339- (NSView *) tabViewForTag: (int) tag
1340{
1341    switch (tag)
1342    {
1343        case TAB_INFO_TAG:
1344            return fInfoView;
1345        case TAB_ACTIVITY_TAG:
1346            return fActivityView;
1347        case TAB_PEERS_TAG:
1348            return fPeersView;
1349        case TAB_FILES_TAG:
1350            return fFilesView;
1351        case TAB_OPTIONS_TAG:
1352            return fOptionsView;
1353        default:
1354            return nil;
1355    }
1356}
1357
1358- (NSArray *) peerSortDescriptors
1359{
1360    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1361   
1362    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1363    BOOL useSecond = YES, asc = YES;
1364    if ([oldDescriptors count] > 0)
1365    {
1366        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1367        [descriptors addObject: descriptor];
1368       
1369        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1370            asc = [descriptor ascending];
1371    }
1372   
1373    //sort by IP after primary sort
1374    if (useSecond)
1375    {
1376        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1377                                                                        selector: @selector(compareIP:)];
1378        [descriptors addObject: secondDescriptor];
1379        [secondDescriptor release];
1380    }
1381   
1382    return descriptors;
1383}
1384
1385@end
Note: See TracBrowser for help on using the repository browser.