source: trunk/macosx/InfoWindowController.m @ 3304

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

get rid of a few redundant variables

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