source: trunk/macosx/InfoWindowController.m @ 3250

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

remove "connected dot" from peer inspector, and append speed string properly to torrents with errors

  • Property svn:keywords set to Date Rev Author Id
File size: 46.7 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 3250 2007-10-01 11:34:01Z 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 indexOfTabViewItem: [fTabView selectedTabViewItem]] == [fTabView numberOfTabViewItems] - 1)
772        [fTabView selectFirstTabViewItem: nil];
773    else
774        [fTabView selectNextTabViewItem: nil];
775}
776
777- (void) setPreviousTab
778{
779    if ([fTabView indexOfTabViewItem: [fTabView selectedTabViewItem]] == 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:
858                                NSLocalizedString(@"Progress: %.1f%%", "Inspector -> Peers tab -> table row tooltip"),
859                                [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
860       
861        if ([[peer objectForKey: @"Encryption"] boolValue])
862            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
863       
864        int port;
865        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
866            [components addObject: [NSString stringWithFormat:
867                                    NSLocalizedString(@"Port: %d", "Inspector -> Peers tab -> table row tooltip"), port]];
868        else
869            [components addObject: NSLocalizedString(@"Port: N/A", "Inspector -> Peers tab -> table row tooltip")];
870       
871        int from = [[peer objectForKey: @"From"] intValue];
872        if (from == TR_PEER_FROM_INCOMING)
873            [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
874        else if (from == TR_PEER_FROM_CACHE)
875            [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
876        else if (from == TR_PEER_FROM_PEX)
877            [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
878        else
879            [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
880       
881        return [components componentsJoinedByString: @"\n"];
882    }
883    return nil;
884}
885
886- (NSArray *) peerSortDescriptors
887{
888    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
889   
890    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
891    BOOL useSecond = YES, asc = YES;
892    if ([oldDescriptors count] > 0)
893    {
894        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
895        [descriptors addObject: descriptor];
896       
897        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
898            asc = [descriptor ascending];
899    }
900   
901    //sort by IP after primary sort
902    if (useSecond)
903    {
904        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
905                                                                        selector: @selector(compareIP:)];
906        [descriptors addObject: secondDescriptor];
907        [secondDescriptor release];
908    }
909   
910    return descriptors;
911}
912
913- (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
914{
915    if (!item)
916        return [fFiles count];
917    return [[item objectForKey: @"IsFolder"] boolValue] ? [[item objectForKey: @"Children"] count] : 0;
918}
919
920- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
921{
922    return [[item objectForKey: @"IsFolder"] boolValue];
923}
924
925- (id) outlineView: (NSOutlineView *) outlineView child: (int) index ofItem: (id) item
926{
927    return [(item ? [item objectForKey: @"Children"] : fFiles) objectAtIndex: index];
928}
929
930- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
931{
932    if ([[tableColumn identifier] isEqualToString: @"Check"])
933        return [NSNumber numberWithInt: [[fTorrents objectAtIndex: 0] checkForFiles: [item objectForKey: @"Indexes"]]];
934    else
935        return item;
936}
937
938- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
939            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
940{
941    NSString * identifier = [tableColumn identifier];
942    if ([identifier isEqualToString: @"Check"])
943        [cell setEnabled: [[fTorrents objectAtIndex: 0] canChangeDownloadCheckForFiles: [item objectForKey: @"Indexes"]]];
944    else if ([identifier isEqualToString: @"Priority"])
945        [cell setRepresentedObject: item];
946    else;
947}
948
949- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
950        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
951{
952    NSString * identifier = [tableColumn identifier];
953    if ([identifier isEqualToString: @"Check"])
954    {
955        Torrent * torrent = [fTorrents objectAtIndex: 0];
956        NSIndexSet * indexSet;
957        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
958            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
959        else
960            indexSet = [item objectForKey: @"Indexes"];
961       
962        [torrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
963        [fFileOutline reloadData];
964       
965        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
966    }
967}
968
969- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
970        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
971{
972    NSString * ident = [tableColumn identifier];
973    if ([ident isEqualToString: @"Name"])
974        return [[[fTorrents objectAtIndex: 0] downloadFolder] stringByAppendingPathComponent: [item objectForKey: @"Path"]];
975    else if ([ident isEqualToString: @"Check"])
976    {
977        int check = [cell state];
978        if (check == NSOffState)
979            return NSLocalizedString(@"Don't Download", "Inspector -> files tab -> tooltip");
980        else if (check == NSMixedState)
981            return NSLocalizedString(@"Download Some", "Inspector -> files tab -> tooltip");
982        else
983            return NSLocalizedString(@"Download", "Inspector -> files tab -> tooltip");
984    }
985    else if ([ident isEqualToString: @"Priority"])
986    {
987        NSSet * priorities = [[fTorrents objectAtIndex: 0] filePrioritiesForIndexes: [item objectForKey: @"Indexes"]];
988       
989        int count = [priorities count];
990        if (count == 0)
991            return NSLocalizedString(@"Priority Not Available", "Inspector -> files tab -> tooltip");
992        else if (count > 1)
993            return NSLocalizedString(@"Multiple Priorities", "Inspector -> files tab -> tooltip");
994        else
995        {
996            int priority = [[priorities anyObject] intValue];
997            if (priority == TR_PRI_LOW)
998                return NSLocalizedString(@"Low Priority", "Inspector -> files tab -> tooltip");
999            else if (priority == TR_PRI_HIGH)
1000                return NSLocalizedString(@"High Priority", "Inspector -> files tab -> tooltip");
1001            else
1002                return NSLocalizedString(@"Normal Priority", "Inspector -> files tab -> tooltip");
1003        }
1004    }
1005    else
1006        return nil;
1007}
1008
1009- (float) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
1010{
1011    if ([[item objectForKey: @"IsFolder"] boolValue])
1012        return FILE_ROW_SMALL_HEIGHT;
1013    else
1014        return [outlineView rowHeight];
1015}
1016
1017- (void) mouseMoved: (NSEvent *) event
1018{
1019    [fFileOutline setHoverRowForEvent: [[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_FILES_IDENT]
1020                                        ? event : nil];
1021}
1022
1023- (void) setPiecesView: (id) sender
1024{
1025    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
1026}
1027
1028- (void) setPiecesViewForAvailable: (BOOL) available
1029{
1030    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
1031    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
1032   
1033    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
1034    [fPiecesView updateView: YES];
1035}
1036
1037- (void) revealTorrentFile: (id) sender
1038{
1039    if ([fTorrents count] > 0)
1040        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
1041}
1042
1043- (void) revealDataFile: (id) sender
1044{
1045    if ([fTorrents count] > 0)
1046        [[fTorrents objectAtIndex: 0] revealData];
1047}
1048
1049- (void) revealFile: (id) sender
1050{
1051    if (!fFiles)
1052        return;
1053   
1054    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
1055    NSIndexSet * indexes = [fFileOutline selectedRowIndexes];
1056    int i;
1057    for (i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
1058        [[NSWorkspace sharedWorkspace] selectFile: [folder stringByAppendingPathComponent:
1059                [[fFileOutline itemAtRow: i] objectForKey: @"Path"]] inFileViewerRootedAtPath: nil];
1060}
1061
1062- (void) setCheck: (id) sender
1063{
1064    int state = sender == fFileCheckItem ? NSOnState : NSOffState;
1065   
1066    Torrent * torrent = [fTorrents objectAtIndex: 0];
1067    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1068    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1069    int i;
1070    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1071        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1072   
1073    [torrent setFileCheckState: state forIndexes: itemIndexes];
1074    [fFileOutline reloadData];
1075}
1076
1077- (void) setOnlySelectedCheck: (id) sender
1078{
1079    Torrent * torrent = [fTorrents objectAtIndex: 0];
1080    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1081    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1082    int i;
1083    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1084        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1085   
1086    [torrent setFileCheckState: NSOnState forIndexes: itemIndexes];
1087   
1088    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
1089    [remainingItemIndexes removeIndexes: itemIndexes];
1090    [torrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
1091   
1092    [fFileOutline reloadData];
1093}
1094
1095- (void) setPriority: (id) sender
1096{
1097    int priority;
1098    if (sender == fFilePriorityHigh)
1099        priority = TR_PRI_HIGH;
1100    else if (sender == fFilePriorityLow)
1101        priority = TR_PRI_LOW;
1102    else
1103        priority = TR_PRI_NORMAL;
1104   
1105    Torrent * torrent = [fTorrents objectAtIndex: 0];
1106    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1107    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1108    int i;
1109    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1110        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1111   
1112    [torrent setFilePriority: priority forIndexes: itemIndexes];
1113    [fFileOutline reloadData];
1114}
1115
1116- (void) setSpeedMode: (id) sender
1117{
1118    BOOL upload = sender == fUploadLimitPopUp;
1119    int index = [sender indexOfSelectedItem], mode;
1120    if (index == OPTION_POPUP_LIMIT)
1121        mode = TR_SPEEDLIMIT_SINGLE;
1122    else if (index == OPTION_POPUP_NO_LIMIT)
1123        mode = TR_SPEEDLIMIT_UNLIMITED;
1124    else
1125        mode = TR_SPEEDLIMIT_GLOBAL;
1126   
1127    Torrent * torrent;
1128    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1129    while ((torrent = [enumerator nextObject]))
1130        [torrent setSpeedMode: mode upload: upload];
1131   
1132    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
1133   
1134    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
1135    [field setHidden: !single];
1136    if (single)
1137    {
1138        [field selectText: self];
1139        [[self window] makeKeyAndOrderFront:self];
1140    }
1141   
1142    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
1143    [label setHidden: !single];
1144}
1145
1146- (void) setSpeedLimit: (id) sender
1147{
1148    BOOL upload = sender == fUploadLimitField;
1149   
1150    Torrent * torrent;
1151    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1152
1153    int limit = [sender intValue];
1154    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%i", limit]] || limit < 0)
1155    {
1156        NSBeep();
1157       
1158        torrent = [enumerator nextObject]; //use first torrent
1159        limit = [torrent speedLimit: upload];
1160        while ((torrent = [enumerator nextObject]))
1161            if (limit != [torrent speedLimit: upload])
1162            {
1163                [sender setStringValue: @""];
1164                return;
1165            }
1166       
1167        [sender setIntValue: limit];
1168    }
1169    else
1170    {
1171        while ((torrent = [enumerator nextObject]))
1172            [torrent setSpeedLimit: limit upload: upload];
1173    }
1174}
1175
1176- (void) setRatioSetting: (id) sender
1177{
1178    int index = [sender indexOfSelectedItem], setting;
1179    if (index == OPTION_POPUP_LIMIT)
1180        setting = NSOnState;
1181    else if (index == OPTION_POPUP_NO_LIMIT)
1182        setting = NSOffState;
1183    else
1184        setting = NSMixedState;
1185   
1186    Torrent * torrent;
1187    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1188    while ((torrent = [enumerator nextObject]))
1189        [torrent setRatioSetting: setting];
1190   
1191    BOOL single = setting == NSOnState;
1192    [fRatioLimitField setHidden: !single];
1193    if (single)
1194    {
1195        [fRatioLimitField selectText: self];
1196        [[self window] makeKeyAndOrderFront:self];
1197    }
1198   
1199    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1200}
1201
1202- (void) setRatioLimit: (id) sender
1203{
1204    Torrent * torrent;
1205    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1206
1207    float ratioLimit = [sender floatValue];
1208    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]] || ratioLimit < 0)
1209    {
1210        NSBeep();
1211        float ratioLimit = [[enumerator nextObject] ratioLimit]; //use first torrent
1212        while ((torrent = [enumerator nextObject]))
1213            if (ratioLimit != [torrent ratioLimit])
1214            {
1215                [sender setStringValue: @""];
1216                return;
1217            }
1218       
1219        [sender setFloatValue: ratioLimit];
1220    }
1221    else
1222    {
1223        while ((torrent = [enumerator nextObject]))
1224            [torrent setRatioLimit: ratioLimit];
1225    }
1226   
1227    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1228}
1229
1230- (void) setPex: (id) sender
1231{
1232        int state = [sender state];
1233        if (state == NSMixedState)
1234        {
1235                state = NSOnState;
1236                [sender setState: state];
1237        }
1238       
1239        Torrent * torrent;
1240    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1241       
1242        while ((torrent = [enumerator nextObject]))
1243                [torrent setPex: state == NSOnState];
1244}
1245
1246@end
Note: See TracBrowser for help on using the repository browser.