source: trunk/macosx/InfoWindowController.m @ 3298

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

file priorities inspector column works again

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