source: branches/0.9x/macosx/InfoWindowController.m @ 3593

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

typing in the file table will select the appropriate item in Leopard

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