source: trunk/macosx/InfoWindowController.m @ 3863

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

have the info controller format the peer status string, for efficiency sakes

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