source: trunk/macosx/InfoWindowController.m @ 3700

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

commit some changes that have been sitting in by checkout

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