source: trunk/macosx/InfoWindowController.m @ 3063

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

get inspector ready for encryption

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