source: trunk/macosx/InfoWindowController.m @ 3597

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

on Leopard us the built-in delegate to select a row while typing, and make Tiger's behavior similar to the new behavior

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