source: trunk/macosx/InfoWindowController.m @ 3314

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

change some initializers so that they are aware of their own nib's name

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