source: trunk/macosx/InfoWindowController.m @ 3865

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

add new peer status enum to gui

  • Property svn:keywords set to Date Rev Author Id
File size: 54.6 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 3865 2007-11-18 01:03:58Z 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 & 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                case TR_PEER_STATUS_ACTIVE_AND_CHOKED:
821                    return NSLocalizedString(@"Active & Interested", "peer -> status");
822            }
823        }
824        else
825            return [peer objectForKey: @"IP"];
826    }
827    return nil;
828}
829
830- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
831{
832    if (tableView == fPeerTable)
833    {
834        if (fPeers)
835        {
836            NSArray * oldPeers = fPeers;
837            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
838            [oldPeers release];
839            [tableView reloadData];
840        }
841    }
842}
843
844- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) row
845{
846    return tableView != fPeerTable;
847}
848
849- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
850                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
851{
852    if (tableView == fPeerTable)
853    {
854        NSDictionary * peer = [fPeers objectAtIndex: row];
855        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
856       
857        [components addObject: [NSString stringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
858            "Inspector -> Peers tab -> table row tooltip"), [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
859       
860        if ([[peer objectForKey: @"Encryption"] boolValue])
861            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
862       
863        int port;
864        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
865            [components addObject: [NSString stringWithFormat:
866                                    NSLocalizedString(@"Port: %d", "Inspector -> Peers tab -> table row tooltip"), port]];
867        else
868            [components addObject: NSLocalizedString(@"Port: N/A", "Inspector -> Peers tab -> table row tooltip")];
869       
870        switch ([[peer objectForKey: @"From"] intValue])
871        {
872            case TR_PEER_FROM_TRACKER:
873                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
874                break;
875            case TR_PEER_FROM_INCOMING:
876                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
877                break;
878            case TR_PEER_FROM_CACHE:
879                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
880                break;
881            case TR_PEER_FROM_PEX:
882                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
883                break;
884        }
885       
886        return [components componentsJoinedByString: @"\n"];
887    }
888    return nil;
889}
890
891- (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
892{
893    if (!item)
894        return [fFiles count];
895    return [[item objectForKey: @"IsFolder"] boolValue] ? [[item objectForKey: @"Children"] count] : 0;
896}
897
898- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
899{
900    return [[item objectForKey: @"IsFolder"] boolValue];
901}
902
903- (id) outlineView: (NSOutlineView *) outlineView child: (int) index ofItem: (id) item
904{
905    return [(item ? [item objectForKey: @"Children"] : fFiles) objectAtIndex: index];
906}
907
908- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
909{
910    if ([[tableColumn identifier] isEqualToString: @"Check"])
911        return [NSNumber numberWithInt: [[fTorrents objectAtIndex: 0] checkForFiles: [item objectForKey: @"Indexes"]]];
912    else
913        return item;
914}
915
916- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
917            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
918{
919    NSString * identifier = [tableColumn identifier];
920    if ([identifier isEqualToString: @"Check"])
921        [cell setEnabled: [[fTorrents objectAtIndex: 0] canChangeDownloadCheckForFiles: [item objectForKey: @"Indexes"]]];
922    else if ([identifier isEqualToString: @"Priority"])
923        [cell setRepresentedObject: item];
924    else;
925}
926
927- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
928        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
929{
930    NSString * identifier = [tableColumn identifier];
931    if ([identifier isEqualToString: @"Check"])
932    {
933        Torrent * torrent = [fTorrents objectAtIndex: 0];
934        NSIndexSet * indexSet;
935        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
936            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
937        else
938            indexSet = [item objectForKey: @"Indexes"];
939       
940        [torrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
941        [fFileOutline reloadData];
942       
943        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
944    }
945}
946
947- (NSString *) outlineView: (NSOutlineView *) outlineView typeSelectStringForTableColumn: (NSTableColumn *) tableColumn item: (id) item
948{
949    return [item objectForKey: @"Name"];
950}
951
952- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
953        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
954{
955    NSString * ident = [tableColumn identifier];
956    if ([ident isEqualToString: @"Name"])
957        return [[[fTorrents objectAtIndex: 0] downloadFolder] stringByAppendingPathComponent: [item objectForKey: @"Path"]];
958    else if ([ident isEqualToString: @"Check"])
959    {
960        switch ([cell state])
961        {
962            case NSOffState:
963                return NSLocalizedString(@"Don't Download", "Inspector -> files tab -> tooltip");
964            case NSOnState:
965                return NSLocalizedString(@"Download", "Inspector -> files tab -> tooltip");
966            case NSMixedState:
967                return NSLocalizedString(@"Download Some", "Inspector -> files tab -> tooltip");
968        }
969    }
970    else if ([ident isEqualToString: @"Priority"])
971    {
972        NSSet * priorities = [[fTorrents objectAtIndex: 0] filePrioritiesForIndexes: [item objectForKey: @"Indexes"]];
973        switch([priorities count])
974        {
975            case 0:
976                return NSLocalizedString(@"Priority Not Available", "Inspector -> files tab -> tooltip");
977            case 1:
978                switch ([[priorities anyObject] intValue])
979                {
980                    case TR_PRI_LOW:
981                        return NSLocalizedString(@"Low Priority", "Inspector -> files tab -> tooltip");
982                    case TR_PRI_HIGH:
983                        return NSLocalizedString(@"High Priority", "Inspector -> files tab -> tooltip");
984                    case TR_PRI_NORMAL:
985                        return NSLocalizedString(@"Normal Priority", "Inspector -> files tab -> tooltip");
986                }
987                break;
988            default:
989                return NSLocalizedString(@"Multiple Priorities", "Inspector -> files tab -> tooltip");
990        }
991    }
992    else;
993   
994    return nil;
995}
996
997- (float) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
998{
999    if ([[item objectForKey: @"IsFolder"] boolValue])
1000        return FILE_ROW_SMALL_HEIGHT;
1001    else
1002        return [outlineView rowHeight];
1003}
1004
1005- (void) mouseMoved: (NSEvent *) event
1006{
1007    [fFileOutline setHoverRowForEvent: fCurrentTabTag == TAB_FILES_TAG ? event : nil];
1008}
1009
1010- (void) setPiecesView: (id) sender
1011{
1012    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
1013}
1014
1015- (void) setPiecesViewForAvailable: (BOOL) available
1016{
1017    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
1018    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
1019   
1020    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
1021    [fPiecesView updateView: YES];
1022}
1023
1024- (void) revealTorrentFile: (id) sender
1025{
1026    if ([fTorrents count] > 0)
1027        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
1028}
1029
1030- (void) revealDataFile: (id) sender
1031{
1032    if ([fTorrents count] > 0)
1033        [[fTorrents objectAtIndex: 0] revealData];
1034}
1035
1036- (void) revealFile: (id) sender
1037{
1038    if (!fFiles)
1039        return;
1040   
1041    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
1042    NSIndexSet * indexes = [fFileOutline selectedRowIndexes];
1043    int i;
1044    for (i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
1045        [[NSWorkspace sharedWorkspace] selectFile: [folder stringByAppendingPathComponent:
1046                [[fFileOutline itemAtRow: i] objectForKey: @"Path"]] inFileViewerRootedAtPath: nil];
1047}
1048
1049- (void) setCheck: (id) sender
1050{
1051    int state = sender == fFileCheckItem ? NSOnState : NSOffState;
1052   
1053    Torrent * torrent = [fTorrents objectAtIndex: 0];
1054    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1055    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1056    int i;
1057    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1058        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1059   
1060    [torrent setFileCheckState: state forIndexes: itemIndexes];
1061    [fFileOutline reloadData];
1062}
1063
1064- (void) setOnlySelectedCheck: (id) sender
1065{
1066    Torrent * torrent = [fTorrents objectAtIndex: 0];
1067    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1068    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1069    int i;
1070    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1071        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1072   
1073    [torrent setFileCheckState: NSOnState forIndexes: itemIndexes];
1074   
1075    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
1076    [remainingItemIndexes removeIndexes: itemIndexes];
1077    [torrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
1078   
1079    [fFileOutline reloadData];
1080}
1081
1082- (void) setPriority: (id) sender
1083{
1084    int priority;
1085    if (sender == fFilePriorityHigh)
1086        priority = TR_PRI_HIGH;
1087    else if (sender == fFilePriorityLow)
1088        priority = TR_PRI_LOW;
1089    else
1090        priority = TR_PRI_NORMAL;
1091   
1092    Torrent * torrent = [fTorrents objectAtIndex: 0];
1093    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1094    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1095    int i;
1096    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1097        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1098   
1099    [torrent setFilePriority: priority forIndexes: itemIndexes];
1100    [fFileOutline reloadData];
1101}
1102
1103- (void) setSpeedMode: (id) sender
1104{
1105    BOOL upload = sender == fUploadLimitPopUp;
1106    int mode;
1107    switch ([sender indexOfSelectedItem])
1108    {
1109        case OPTION_POPUP_LIMIT:
1110            mode = TR_SPEEDLIMIT_SINGLE;
1111            break;
1112        case OPTION_POPUP_NO_LIMIT:
1113            mode = TR_SPEEDLIMIT_UNLIMITED;
1114            break;
1115        case OPTION_POPUP_GLOBAL:
1116            mode = TR_SPEEDLIMIT_GLOBAL;
1117            break;
1118        default:
1119            return;
1120    }
1121   
1122    Torrent * torrent;
1123    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1124    while ((torrent = [enumerator nextObject]))
1125        [torrent setSpeedMode: mode upload: upload];
1126   
1127    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
1128   
1129    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
1130    [field setHidden: !single];
1131    if (single)
1132    {
1133        [field selectText: self];
1134        [[self window] makeKeyAndOrderFront:self];
1135    }
1136   
1137    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
1138    [label setHidden: !single];
1139}
1140
1141- (void) setSpeedLimit: (id) sender
1142{
1143    BOOL upload = sender == fUploadLimitField;
1144   
1145    Torrent * torrent;
1146    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1147
1148    int limit = [sender intValue];
1149    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%i", limit]] || limit < 0)
1150    {
1151        NSBeep();
1152       
1153        torrent = [enumerator nextObject]; //use first torrent
1154        limit = [torrent speedLimit: upload];
1155        while ((torrent = [enumerator nextObject]))
1156            if (limit != [torrent speedLimit: upload])
1157            {
1158                [sender setStringValue: @""];
1159                return;
1160            }
1161       
1162        [sender setIntValue: limit];
1163    }
1164    else
1165    {
1166        while ((torrent = [enumerator nextObject]))
1167            [torrent setSpeedLimit: limit upload: upload];
1168    }
1169}
1170
1171- (void) setRatioSetting: (id) sender
1172{
1173    int setting;
1174    switch ([sender indexOfSelectedItem])
1175    {
1176        case OPTION_POPUP_LIMIT:
1177            setting = NSOnState;
1178            break;
1179        case OPTION_POPUP_NO_LIMIT:
1180            setting = NSOffState;
1181            break;
1182        case OPTION_POPUP_GLOBAL:
1183            setting = NSMixedState;
1184            break;
1185        default:
1186            return;
1187    }
1188   
1189    Torrent * torrent;
1190    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1191    while ((torrent = [enumerator nextObject]))
1192        [torrent setRatioSetting: setting];
1193   
1194    BOOL single = setting == NSOnState;
1195    [fRatioLimitField setHidden: !single];
1196    if (single)
1197    {
1198        [fRatioLimitField selectText: self];
1199        [[self window] makeKeyAndOrderFront:self];
1200    }
1201   
1202    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1203}
1204
1205- (void) setRatioLimit: (id) sender
1206{
1207    Torrent * torrent;
1208    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1209
1210    float ratioLimit = [sender floatValue];
1211    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]] || ratioLimit < 0)
1212    {
1213        NSBeep();
1214        float ratioLimit = [[enumerator nextObject] ratioLimit]; //use first torrent
1215        while ((torrent = [enumerator nextObject]))
1216            if (ratioLimit != [torrent ratioLimit])
1217            {
1218                [sender setStringValue: @""];
1219                return;
1220            }
1221       
1222        [sender setFloatValue: ratioLimit];
1223    }
1224    else
1225    {
1226        while ((torrent = [enumerator nextObject]))
1227            [torrent setRatioLimit: ratioLimit];
1228    }
1229   
1230    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1231}
1232
1233- (void) setPex: (id) sender
1234{
1235        int state = [sender state];
1236        if (state == NSMixedState)
1237        {
1238                state = NSOnState;
1239                [sender setState: state];
1240        }
1241       
1242        Torrent * torrent;
1243    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1244       
1245        while ((torrent = [enumerator nextObject]))
1246                [torrent setPex: state == NSOnState];
1247}
1248
1249@end
1250
1251@implementation InfoWindowController (Private)
1252
1253- (void) updateInfoGeneral
1254{   
1255    if ([fTorrents count] != 1)
1256        return;
1257   
1258    Torrent * torrent = [fTorrents objectAtIndex: 0];
1259   
1260    NSString * tracker = [[torrent trackerAddress] stringByAppendingString: [torrent trackerAddressAnnounce]];
1261    [fTrackerField setStringValue: tracker];
1262   
1263    NSString * location = [torrent dataLocation];
1264    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1265    [fDataLocationField setToolTip: location];
1266}
1267
1268- (void) updateInfoActivity
1269{
1270    int numberSelected = [fTorrents count];
1271    if (numberSelected == 0)
1272        return;
1273   
1274    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1275    Torrent * torrent;
1276    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1277    while ((torrent = [enumerator nextObject]))
1278    {
1279        have += [torrent haveTotal];
1280        haveVerified += [torrent haveVerified];
1281        downloadedTotal += [torrent downloadedTotal];
1282        uploadedTotal += [torrent uploadedTotal];
1283        failedHash += [torrent failedHash];
1284    }
1285   
1286    if (have == 0)
1287        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1288    else if (have == haveVerified)
1289        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ verified",
1290                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: haveVerified]]];
1291    else
1292        [fHaveField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ (%@ verified)",
1293                "Inspector -> Activity tab -> have"), [NSString stringForFileSize: have], [NSString stringForFileSize: haveVerified]]];
1294   
1295    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1296    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1297    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1298   
1299    if (numberSelected == 1)
1300    {
1301        torrent = [fTorrents objectAtIndex: 0];
1302       
1303        [fStateField setStringValue: [torrent stateString]];
1304        [fProgressField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1305                    "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1306        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1307        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1308       
1309        NSString * errorMessage = [torrent errorMessage];
1310        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1311        {
1312            [fErrorMessageView setString: errorMessage];
1313            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1314        }
1315       
1316        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1317        [fDateActivityField setObjectValue: [torrent dateActivity]];
1318       
1319        [fPiecesView updateView: NO];
1320    }
1321}
1322
1323- (void) updateInfoPeers
1324{
1325    if ([fTorrents count] != 1)
1326        return;
1327    Torrent * torrent = [fTorrents objectAtIndex: 0];
1328   
1329    int seeders = [torrent seeders], leechers = [torrent leechers], completed = [torrent completedFromTracker];
1330    [fSeedersField setStringValue: seeders >= 0 ? [NSString stringWithFormat: @"%d", seeders] : @""];
1331    [fLeechersField setStringValue: leechers >= 0 ? [NSString stringWithFormat: @"%d", leechers] : @""];
1332    [fCompletedFromTrackerField setStringValue: completed >= 0 ? [NSString stringWithFormat: @"%d", completed] : @""];
1333   
1334    BOOL active = [torrent isActive];
1335   
1336    if (active)
1337    {
1338        int total = [torrent totalPeersConnected];
1339        NSString * connected = [NSString stringWithFormat:
1340                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1341       
1342        if (total > 0)
1343        {
1344            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
1345            int count;
1346            if ((count = [torrent totalPeersTracker]) > 0)
1347                [components addObject: [NSString stringWithFormat:
1348                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1349            if ((count = [torrent totalPeersIncoming]) > 0)
1350                [components addObject: [NSString stringWithFormat:
1351                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1352            if ((count = [torrent totalPeersPex]) > 0)
1353                [components addObject: [NSString stringWithFormat:
1354                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1355            if ((count = [torrent totalPeersCache]) > 0)
1356                [components addObject: [NSString stringWithFormat:
1357                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1358           
1359            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1360        }
1361       
1362        [fConnectedPeersField setStringValue: connected];
1363       
1364        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1365        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1366    }
1367    else
1368    {
1369        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
1370        [fDownloadingFromField setStringValue: @""];
1371        [fUploadingToField setStringValue: @""];
1372    }
1373   
1374    [fKnownField setIntValue: [torrent totalPeersKnown]];
1375   
1376    [fPeers release];
1377    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1378   
1379    [fPeerTable reloadData];
1380}
1381
1382- (void) updateInfoFiles
1383{
1384    if ([fTorrents count] == 1)
1385    {
1386        [[fTorrents objectAtIndex: 0] updateFileStat];
1387        [fFileOutline reloadData];
1388    }
1389}
1390
1391- (void) updateInfoOptions
1392{
1393    if ([fTorrents count] == 0)
1394        return;
1395   
1396    //set pex check
1397    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1398    Torrent * torrent = [enumerator nextObject]; //first torrent
1399   
1400    BOOL pexEnabled = ![torrent privateTorrent] && ![torrent isActive];
1401    int pexState = [torrent pex] ? NSOnState : NSOffState;
1402   
1403    while ((torrent = [enumerator nextObject]) && (pexEnabled || pexState != NSMixedState))
1404    {
1405        if (pexEnabled)
1406            pexEnabled = ![torrent privateTorrent] && ![torrent isActive];
1407       
1408        if (pexState != NSMixedState && pexState != ([torrent pex] ? NSOnState : NSOffState))
1409            pexState = NSMixedState;
1410    }
1411   
1412    [fPexCheck setEnabled: pexEnabled];
1413    [fPexCheck setState: pexState];
1414    [fPexCheck setToolTip: !pexEnabled ? NSLocalizedString(@"PEX can only be toggled on paused public torrents.",
1415                                "Inspector -> pex check") : nil];
1416}
1417
1418- (NSView *) tabViewForTag: (int) tag
1419{
1420    switch (tag)
1421    {
1422        case TAB_INFO_TAG:
1423            return fInfoView;
1424        case TAB_ACTIVITY_TAG:
1425            return fActivityView;
1426        case TAB_PEERS_TAG:
1427            return fPeersView;
1428        case TAB_FILES_TAG:
1429            return fFilesView;
1430        case TAB_OPTIONS_TAG:
1431            return fOptionsView;
1432        default:
1433            return nil;
1434    }
1435}
1436
1437- (NSArray *) peerSortDescriptors
1438{
1439    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1440   
1441    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1442    BOOL useSecond = YES, asc = YES;
1443    if ([oldDescriptors count] > 0)
1444    {
1445        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1446        [descriptors addObject: descriptor];
1447       
1448        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1449            asc = [descriptor ascending];
1450    }
1451   
1452    //sort by IP after primary sort
1453    if (useSecond)
1454    {
1455        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1456                                                                        selector: @selector(compareIP:)];
1457        [descriptors addObject: secondDescriptor];
1458        [secondDescriptor release];
1459    }
1460   
1461    return descriptors;
1462}
1463
1464@end
Note: See TracBrowser for help on using the repository browser.