source: trunk/macosx/InfoWindowController.m @ 3638

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

trunk: Leopard: Fix bug with typing values in Inspector->Options

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