source: trunk/macosx/InfoWindowController.m @ 3300

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

get selected tab in inspector closer to working: 1. it doesn't select at startup 2. needs missing images and cleaned up back images

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