source: trunk/macosx/InfoWindowController.m @ 3301

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

clean up a little inspector code

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