source: trunk/macosx/InfoWindowController.m @ 3316

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

get rid of some warnings

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