source: trunk/macosx/InfoWindowController.m @ 3345

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

improve Activity icon and display the tab name in the inspector's title

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