source: trunk/macosx/InfoWindowController.m @ 3259

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

add known peers field to the inspector

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