source: trunk/macosx/InfoWindowController.m @ 3252

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

update strings file

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