source: trunk/macosx/InfoWindowController.m @ 5867

Last change on this file since 5867 was 5867, checked in by livings124, 14 years ago

update quicklook when switch tabs to the file tab

  • Property svn:keywords set to Date Rev Author Id
File size: 52.7 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 5867 2008-05-20 23:57:42Z livings124 $
3 *
4 * Copyright (c) 2006-2008 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 "NSApplicationAdditions.h"
28#import "NSStringAdditions.h"
29
30#import "QuickLook.h"
31#define QLPreviewPanel NSClassFromString(@"QLPreviewPanel")
32
33#define TAB_INFO_IDENT @"Info"
34#define TAB_ACTIVITY_IDENT @"Activity"
35#define TAB_TRACKER_IDENT @"Tracker"
36#define TAB_PEERS_IDENT @"Peers"
37#define TAB_FILES_IDENT @"Files"
38#define TAB_OPTIONS_IDENT @"Options"
39
40#define TAB_MIN_HEIGHT 250
41
42#define PIECES_CONTROL_PROGRESS 0
43#define PIECES_CONTROL_AVAILABLE 1
44
45#define OPTION_POPUP_GLOBAL 0
46#define OPTION_POPUP_NO_LIMIT 1
47#define OPTION_POPUP_LIMIT 2
48
49#define INVALID -99
50
51typedef enum
52{
53    TAB_INFO_TAG = 0,
54    TAB_ACTIVITY_TAG = 1,
55    TAB_TRACKER_TAG = 2,
56    TAB_PEERS_TAG = 3,
57    TAB_FILES_TAG = 4,
58    TAB_OPTIONS_TAG = 5
59} tabTag;
60
61@interface InfoWindowController (Private)
62
63- (void) updateInfoGeneral;
64- (void) updateInfoActivity;
65- (void) updateInfoTracker;
66- (void) updateInfoPeers;
67- (void) updateInfoFiles;
68
69- (NSView *) tabViewForTag: (int) tag;
70- (NSArray *) peerSortDescriptors;
71
72@end
73
74@implementation InfoWindowController
75
76- (id) init
77{
78    return [super initWithWindowNibName: @"InfoWindow"];
79}
80
81- (void) awakeFromNib
82{
83    //window location and size
84    NSPanel * window = (NSPanel *)[self window];
85   
86    float windowHeight = [window frame].size.height;
87   
88    [window setFrameAutosaveName: @"InspectorWindow"];
89    [window setFrameUsingName: @"InspectorWindow"];
90   
91    NSRect windowRect = [window frame];
92    windowRect.origin.y -= windowHeight - windowRect.size.height;
93    windowRect.size.height = windowHeight;
94    [window setFrame: windowRect display: NO];
95   
96    [window setBecomesKeyOnlyIfNeeded: YES];
97   
98    //set tab images and tooltips
99    [[fTabMatrix cellWithTag: TAB_INFO_TAG] setIcon: [NSImage imageNamed: @"InfoGeneral.png"]];
100    [[fTabMatrix cellWithTag: TAB_ACTIVITY_TAG] setIcon: [NSImage imageNamed: @"InfoActivity.png"]];
101    [[fTabMatrix cellWithTag: TAB_TRACKER_TAG] setIcon: [NSImage imageNamed: @"InfoTracker.png"]];
102    [[fTabMatrix cellWithTag: TAB_PEERS_TAG] setIcon: [NSImage imageNamed: @"InfoPeers.png"]];
103    [[fTabMatrix cellWithTag: TAB_FILES_TAG] setIcon: [NSImage imageNamed: @"InfoFiles.png"]];
104    [[fTabMatrix cellWithTag: TAB_OPTIONS_TAG] setIcon: [NSImage imageNamed: @"InfoOptions.png"]];
105   
106    //set selected tab
107    fCurrentTabTag = INVALID;
108    NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"];
109    int tag;
110    if ([identifier isEqualToString: TAB_INFO_IDENT])
111        tag = TAB_INFO_TAG;
112    else if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
113        tag = TAB_ACTIVITY_TAG;
114    else if ([identifier isEqualToString: TAB_TRACKER_IDENT])
115        tag = TAB_TRACKER_TAG;
116    else if ([identifier isEqualToString: TAB_PEERS_IDENT])
117        tag = TAB_PEERS_TAG;
118    else if ([identifier isEqualToString: TAB_FILES_IDENT])
119        tag = TAB_FILES_TAG;
120    else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
121        tag = TAB_OPTIONS_TAG;
122    else //safety
123    {
124        [[NSUserDefaults standardUserDefaults] setObject: TAB_INFO_IDENT forKey: @"InspectorSelected"];
125        tag = TAB_INFO_TAG;
126    }
127    [fTabMatrix selectCellWithTag: tag];
128    [self setTab: nil];
129   
130    //reset images for reveal buttons, since the images are also used in the main table
131    NSImage * revealOn = [[NSImage imageNamed: @"RevealOn.png"] copy],
132            * revealOff = [[NSImage imageNamed: @"RevealOff.png"] copy];
133    [revealOn setFlipped: NO];
134    [revealOff setFlipped: NO];
135   
136    [fRevealDataButton setImage: revealOff];
137    [fRevealDataButton setAlternateImage: revealOn];
138    [fRevealTorrentButton setImage: revealOff];
139    [fRevealTorrentButton setAlternateImage: revealOn];
140   
141    [revealOn release];
142    [revealOff release];
143   
144    //load the QuickLook framework and set the delegate, no point on trying this on Tiger
145    //animation types: 0 = none; 1 = fade; 2 = zoom
146    fQuickLookAvailable = [[NSBundle bundleWithPath: @"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load];
147    if (fQuickLookAvailable)
148        [[[QLPreviewPanel sharedPreviewPanel] windowController] setDelegate: self];
149   
150    //initially sort peer table by IP
151    if ([[fPeerTable sortDescriptors] count] == 0)
152        [fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
153                                            sortDescriptorPrototype]]];
154   
155    //set table header tool tips
156    if ([NSApp isOnLeopardOrBetter])
157    {
158        [[fPeerTable tableColumnWithIdentifier: @"Encryption"] setHeaderToolTip: NSLocalizedString(@"Encrypted Connection",
159                                                                            "inspector -> peer table -> header tool tip")];
160        [[fPeerTable tableColumnWithIdentifier: @"Progress"] setHeaderToolTip: NSLocalizedString(@"Available",
161                                                                            "inspector -> peer table -> header tool tip")];
162        [[fPeerTable tableColumnWithIdentifier: @"UL To"] setHeaderToolTip: NSLocalizedString(@"Uploading To Peer",
163                                                                            "inspector -> peer table -> header tool tip")];
164        [[fPeerTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Peer",
165                                                                            "inspector -> peer table -> header tool tip")];                                                             
166    }
167   
168    //set blank inspector
169    [self setInfoForTorrents: [NSArray array]];
170   
171    //allow for update notifications
172    NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
173    [nc addObserver: self selector: @selector(updateInfoStats) name: @"UpdateStats" object: nil];
174    [nc addObserver: self selector: @selector(updateOptions) name: @"UpdateOptions" object: nil];
175    [nc addObserver: self selector: @selector(updateQuickLook) name: @"UpdateQuickLook" object: nil];
176}
177
178- (void) dealloc
179{
180    //save resizeable view height
181    NSString * resizeSaveKey = nil;
182    switch (fCurrentTabTag)
183    {
184        case TAB_TRACKER_TAG:
185            resizeSaveKey = @"InspectorContentHeightTracker";
186            break;
187        case TAB_PEERS_TAG:
188            resizeSaveKey = @"InspectorContentHeightPeers";
189            break;
190        case TAB_FILES_TAG:
191            resizeSaveKey = @"InspectorContentHeightFiles";
192            break;
193    }
194    if (resizeSaveKey)
195        [[NSUserDefaults standardUserDefaults] setFloat: [[self tabViewForTag: fCurrentTabTag] frame].size.height forKey: resizeSaveKey];
196   
197    [[NSNotificationCenter defaultCenter] removeObserver: self];
198   
199    [fTorrents release];
200    [fPeers release];
201    [fTrackers release];
202   
203    [super dealloc];
204}
205
206- (void) setInfoForTorrents: (NSArray *) torrents
207{
208    if (fTorrents && [fTorrents isEqualToArray: torrents])
209        return;
210   
211    [fTorrents release];
212    fTorrents = [torrents retain];
213
214    int numberSelected = [fTorrents count];
215    if (numberSelected != 1)
216    {
217        if (numberSelected > 0)
218        {
219            [fImageView setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter]
220                                    ? NSImageNameMultipleDocuments : @"NSApplicationIcon"]];
221           
222            [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d Torrents Selected",
223                                            "Inspector -> selected torrents"), numberSelected]];
224       
225            uint64_t size = 0;
226            int fileCount = 0;
227            NSEnumerator * enumerator = [torrents objectEnumerator];
228            Torrent * torrent;
229            while ((torrent = [enumerator nextObject]))
230            {
231                size += [torrent size];
232                fileCount += [torrent fileCount];
233            }
234           
235            [fBasicInfoField setStringValue: [NSString stringWithFormat: @"%@, %@",
236                [NSString stringWithFormat: NSLocalizedString(@"%d files", "Inspector -> selected torrents"), fileCount],
237                [NSString stringWithFormat: NSLocalizedString(@"%@ total", "Inspector -> selected torrents"),
238                [NSString stringForFileSize: size]]]];
239            [fBasicInfoField setToolTip: [NSString stringWithFormat: NSLocalizedString(@"%u bytes", "Inspector -> selected torrents"),
240                                            size]];
241        }
242        else
243        {
244            [fImageView setImage: [NSImage imageNamed: @"NSApplicationIcon"]];
245           
246            [fNameField setStringValue: NSLocalizedString(@"No Torrents Selected", "Inspector -> selected torrents")];
247            [fBasicInfoField setStringValue: @""];
248            [fBasicInfoField setToolTip: @""];
249   
250            [fHaveField setStringValue: @""];
251            [fDownloadedTotalField setStringValue: @""];
252            [fUploadedTotalField setStringValue: @""];
253            [fFailedHashField setStringValue: @""];
254            [fDateActivityField setStringValue: @""];
255           
256            //options fields
257            [fUploadLimitPopUp setEnabled: NO];
258            [fUploadLimitPopUp selectItemAtIndex: -1];
259            [fUploadLimitField setHidden: YES];
260            [fUploadLimitLabel setHidden: YES];
261            [fUploadLimitField setStringValue: @""];
262           
263            [fDownloadLimitPopUp setEnabled: NO];
264            [fDownloadLimitPopUp selectItemAtIndex: -1];
265            [fDownloadLimitField setHidden: YES];
266            [fDownloadLimitLabel setHidden: YES];
267            [fDownloadLimitField setStringValue: @""];
268           
269            [fRatioPopUp setEnabled: NO];
270            [fRatioPopUp selectItemAtIndex: -1];
271            [fRatioLimitField setHidden: YES];
272            [fRatioLimitField setStringValue: @""];
273           
274            [fPeersConnectField setEnabled: NO];
275            [fPeersConnectField setStringValue: @""];
276        }
277       
278        [fFileController setTorrent: nil];
279       
280        [fNameField setToolTip: nil];
281
282        [fTrackerField setStringValue: @""];
283        [fPiecesField setStringValue: @""];
284        [fHashField setStringValue: @""];
285        [fHashField setToolTip: nil];
286        [fSecureField setStringValue: @""];
287        [fCommentView setString: @""];
288       
289        [fCreatorField setStringValue: @""];
290        [fDateCreatedField setStringValue: @""];
291        [fCommentView setSelectable: NO];
292       
293        [fTorrentLocationField setStringValue: @""];
294        [fTorrentLocationField setToolTip: nil];
295        [fDataLocationField setStringValue: @""];
296        [fDataLocationField setToolTip: nil];
297       
298        [fRevealDataButton setHidden: YES];
299        [fRevealTorrentButton setHidden: YES];
300       
301        //don't allow empty fields to be selected
302        [fTrackerField setSelectable: NO];
303        [fHashField setSelectable: NO];
304        [fCreatorField setSelectable: NO];
305        [fTorrentLocationField setSelectable: NO];
306        [fDataLocationField setSelectable: NO];
307       
308        [fStateField setStringValue: @""];
309        [fProgressField setStringValue: @""];
310        [fRatioField setStringValue: @""];
311       
312        [fSwarmSpeedField setStringValue: @""];
313        [fErrorMessageView setString: @""];
314        [fErrorMessageView setSelectable: NO];
315       
316        [fAnnounceAddressField setStringValue: @""];
317        [fAnnounceAddressField setToolTip: nil];
318        [fAnnounceAddressField setSelectable: NO];
319        [fAnnounceLastField setStringValue: @""];
320        [fAnnounceResponseField setStringValue: @""];
321        [fAnnounceResponseField setToolTip: nil];
322        [fAnnounceResponseField setSelectable: NO];
323        [fAnnounceNextField setStringValue: @""];
324       
325        [fScrapeAddressField setStringValue: @""];
326        [fScrapeAddressField setToolTip: nil];
327        [fScrapeAddressField setSelectable: NO];
328        [fScrapeLastField setStringValue: @""];
329        [fScrapeResponseField setStringValue: @""];
330        [fScrapeResponseField setToolTip: nil];
331        [fScrapeResponseField setSelectable: NO];
332        [fScrapeNextField setStringValue: @""];
333       
334        [fConnectedPeersField setStringValue: @""];
335        [fDownloadingFromField setStringValue: @""];
336        [fUploadingToField setStringValue: @""];
337        [fKnownField setStringValue: @""];
338        [fSeedersField setStringValue: @""];
339        [fLeechersField setStringValue: @""];
340        [fCompletedFromTrackerField setStringValue: @""];
341       
342        [fDateAddedField setStringValue: @""];
343        [fDateCompletedField setStringValue: @""];
344       
345        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE];
346        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS];
347        [fPiecesControl setEnabled: NO];
348        [fPiecesView setTorrent: nil];
349       
350        [fPeers release];
351        fPeers = nil;
352       
353        [fTrackers release];
354        fTrackers = nil;
355    }
356    else
357    {   
358        Torrent * torrent = [fTorrents objectAtIndex: 0];
359       
360        [fFileController setTorrent: torrent];
361       
362        NSImage * icon = [[torrent icon] copy];
363        [icon setFlipped: NO];
364        [fImageView setImage: icon];
365        [icon release];
366       
367        NSString * name = [torrent name];
368        [fNameField setStringValue: name];
369        [fNameField setToolTip: name];
370       
371        NSString * basicString = [NSString stringForFileSize: [torrent size]];
372        if ([torrent folder])
373        {
374            NSString * fileString;
375            int fileCount = [torrent fileCount];
376            if (fileCount == 1)
377                fileString = NSLocalizedString(@"1 file", "Inspector -> selected torrents");
378            else
379                fileString= [NSString stringWithFormat: NSLocalizedString(@"%d files", "Inspector -> selected torrents"), fileCount];
380            basicString = [NSString stringWithFormat: @"%@, %@", fileString, basicString];
381        }
382        [fBasicInfoField setStringValue: basicString];
383        [fBasicInfoField setToolTip: [NSString stringWithFormat: NSLocalizedString(@"%u bytes", "Inspector -> selected torrents"),
384                                        [torrent size]]];
385       
386        NSString * hashString = [torrent hashString];
387        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
388                                        [NSString stringForFileSize: [torrent pieceSize]]]];
389        [fHashField setStringValue: hashString];
390        [fHashField setToolTip: hashString];
391        [fSecureField setStringValue: [torrent privateTorrent]
392                        ? NSLocalizedString(@"Private Torrent, PEX automatically disabled", "Inspector -> private torrent")
393                        : NSLocalizedString(@"Public Torrent", "Inspector -> private torrent")];
394       
395        NSString * commentString = [torrent comment];
396        [fCommentView setString: commentString];
397       
398        NSString * creatorString = [torrent creator];
399        [fCreatorField setStringValue: creatorString];
400        [fDateCreatedField setObjectValue: [torrent dateCreated]];
401       
402        BOOL publicTorrent = [torrent publicTorrent];
403        [fTorrentLocationField setStringValue: publicTorrent
404                    ? [[torrent publicTorrentLocation] stringByAbbreviatingWithTildeInPath]
405                    : NSLocalizedString(@"Transmission Support Folder", "Torrent -> location when deleting original")];
406        if (publicTorrent)
407            [fTorrentLocationField setToolTip: [[torrent publicTorrentLocation] stringByAppendingFormat: @"\n\n%@",
408                                                [torrent torrentLocation]]];
409        else
410            [fTorrentLocationField setToolTip: [torrent torrentLocation]];
411       
412        [fDateAddedField setObjectValue: [torrent dateAdded]];
413       
414        [fRevealDataButton setHidden: NO];
415        [fRevealTorrentButton setHidden: ![torrent publicTorrent]];
416       
417        //allow these fields to be selected
418        [fTrackerField setSelectable: YES];
419        [fHashField setSelectable: YES];
420        [fCommentView setSelectable: ![commentString isEqualToString: @""]];
421        [fCreatorField setSelectable: ![creatorString isEqualToString: @""]];
422        [fTorrentLocationField setSelectable: YES];
423        [fDataLocationField setSelectable: YES];
424        [fAnnounceAddressField setSelectable: YES];
425        [fScrapeAddressField setSelectable: YES];
426       
427        //set pieces view
428        BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"];
429        [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE];
430        [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS];
431        [fPiecesControl setEnabled: YES];
432        [fPiecesView setTorrent: torrent];
433       
434        //get trackers for table
435        [fTrackers release];
436        fTrackers = [[torrent allTrackers: YES] retain];
437    }
438   
439    //update stats and settings
440    [self updateInfoStats];
441    [self updateOptions];
442   
443    [fTrackerTable reloadData];
444    [fPeerTable reloadData];
445   
446    [self updateQuickLook];
447}
448
449- (void) updateInfoStats
450{
451    switch ([fTabMatrix selectedTag])
452    {
453        case TAB_INFO_TAG:
454            [self updateInfoGeneral];
455            break;
456        case TAB_ACTIVITY_TAG:
457            [self updateInfoActivity];
458            break;
459        case TAB_TRACKER_TAG:
460            [self updateInfoTracker];
461            break;
462        case TAB_PEERS_TAG:
463            [self updateInfoPeers];
464            break;
465        case TAB_FILES_TAG:
466            [self updateInfoFiles];
467            break;
468    }
469}
470
471- (void) updateOptions
472{
473    if ([fTorrents count] == 0)
474        return;
475   
476    //get bandwidth info
477    NSEnumerator * enumerator = [fTorrents objectEnumerator];
478    Torrent * torrent = [enumerator nextObject]; //first torrent
479   
480    int uploadSpeedMode = [torrent speedMode: YES],
481        uploadSpeedLimit = [torrent speedLimit: YES],
482        downloadSpeedMode = [torrent speedMode: NO],
483        downloadSpeedLimit = [torrent speedLimit: NO];
484   
485    while ((torrent = [enumerator nextObject])
486            && (uploadSpeedMode != INVALID || uploadSpeedLimit != INVALID
487                || downloadSpeedMode != INVALID || downloadSpeedLimit != INVALID))
488    {
489        if (uploadSpeedMode != INVALID && uploadSpeedMode != [torrent speedMode: YES])
490            uploadSpeedMode = INVALID;
491       
492        if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
493            uploadSpeedLimit = INVALID;
494       
495        if (downloadSpeedMode != INVALID && downloadSpeedMode != [torrent speedMode: NO])
496            downloadSpeedMode = INVALID;
497       
498        if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
499            downloadSpeedLimit = INVALID;
500    }
501   
502    //set upload view
503    int index;
504    if (uploadSpeedMode == TR_SPEEDLIMIT_SINGLE)
505        index = OPTION_POPUP_LIMIT;
506    else if (uploadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
507        index = OPTION_POPUP_NO_LIMIT;
508    else if (uploadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
509        index = OPTION_POPUP_GLOBAL;
510    else
511        index = -1;
512    [fUploadLimitPopUp selectItemAtIndex: index];
513    [fUploadLimitPopUp setEnabled: YES];
514   
515    [fUploadLimitLabel setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
516    [fUploadLimitField setHidden: uploadSpeedMode != TR_SPEEDLIMIT_SINGLE];
517    if (uploadSpeedLimit != INVALID)
518        [fUploadLimitField setIntValue: uploadSpeedLimit];
519    else
520        [fUploadLimitField setStringValue: @""];
521   
522    //set download view
523    if (downloadSpeedMode == TR_SPEEDLIMIT_SINGLE)
524        index = OPTION_POPUP_LIMIT;
525    else if (downloadSpeedMode == TR_SPEEDLIMIT_UNLIMITED)
526        index = OPTION_POPUP_NO_LIMIT;
527    else if (downloadSpeedMode == TR_SPEEDLIMIT_GLOBAL)
528        index = OPTION_POPUP_GLOBAL;
529    else
530        index = -1;
531    [fDownloadLimitPopUp selectItemAtIndex: index];
532    [fDownloadLimitPopUp setEnabled: YES];
533   
534    [fDownloadLimitLabel setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
535    [fDownloadLimitField setHidden: downloadSpeedMode != TR_SPEEDLIMIT_SINGLE];
536    if (downloadSpeedLimit != INVALID)
537        [fDownloadLimitField setIntValue: downloadSpeedLimit];
538    else
539        [fDownloadLimitField setStringValue: @""];
540   
541    //get ratio info
542    enumerator = [fTorrents objectEnumerator];
543    torrent = [enumerator nextObject]; //first torrent
544   
545    int checkRatio = [torrent ratioSetting];
546    float ratioLimit = [torrent ratioLimit];
547   
548    while ((torrent = [enumerator nextObject]) && (checkRatio != INVALID || checkRatio != INVALID))
549    {
550        if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
551            checkRatio = INVALID;
552       
553        if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
554            ratioLimit = INVALID;
555    }
556   
557    //set ratio view
558    if (checkRatio == NSOnState)
559        index = OPTION_POPUP_LIMIT;
560    else if (checkRatio == NSOffState)
561        index = OPTION_POPUP_NO_LIMIT;
562    else if (checkRatio == NSMixedState)
563        index = OPTION_POPUP_GLOBAL;
564    else
565        index = -1;
566    [fRatioPopUp selectItemAtIndex: index];
567    [fRatioPopUp setEnabled: YES];
568   
569    [fRatioLimitField setHidden: checkRatio != NSOnState];
570    if (ratioLimit != INVALID)
571        [fRatioLimitField setFloatValue: ratioLimit];
572    else
573        [fRatioLimitField setStringValue: @""];
574   
575    //get peer info
576    enumerator = [fTorrents objectEnumerator];
577    torrent = [enumerator nextObject]; //first torrent
578   
579    int maxPeers = [torrent maxPeerConnect];
580   
581    while ((torrent = [enumerator nextObject]))
582    {
583        if (maxPeers != [torrent maxPeerConnect])
584        {
585            maxPeers = INVALID;
586            break;
587        }
588    }
589   
590    //set peer view
591    [fPeersConnectField setEnabled: YES];
592    if (maxPeers != INVALID)
593        [fPeersConnectField setIntValue: maxPeers];
594    else
595        [fPeersConnectField setStringValue: @""];
596}
597
598- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
599{
600    NSRect windowRect = [window frame];
601    windowRect.size.width = [window minSize].width;
602    return windowRect;
603}
604
605- (void) windowWillClose: (NSNotification *) notification
606{
607    if (fQuickLookAvailable)
608        [[QLPreviewPanel sharedPreviewPanel] closeWithEffect: 0];
609}
610
611- (void) setTab: (id) sender
612{
613    int oldTabTag = fCurrentTabTag;
614    fCurrentTabTag = [fTabMatrix selectedTag];
615    if (fCurrentTabTag == oldTabTag)
616        return;
617   
618    [self updateInfoStats];
619   
620    //take care of old view
621    float oldHeight = 0;
622    NSString * oldResizeSaveKey = nil;
623    if (oldTabTag != INVALID)
624    {
625        //deselect old tab item
626        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
627       
628        switch (oldTabTag)
629        {
630            case TAB_TRACKER_TAG:
631                oldResizeSaveKey = @"InspectorContentHeightTracker";
632                break;
633            case TAB_PEERS_TAG:
634                [fPeers release];
635                fPeers = nil;
636               
637                oldResizeSaveKey = @"InspectorContentHeightPeers";
638                break;
639            case TAB_FILES_TAG:
640                [self updateQuickLook];
641               
642                oldResizeSaveKey = @"InspectorContentHeightFiles";
643                break;
644        }
645       
646        NSView * oldView = [self tabViewForTag: oldTabTag];
647        oldHeight = [oldView frame].size.height;
648        if (oldResizeSaveKey)
649            [[NSUserDefaults standardUserDefaults] setFloat: oldHeight forKey: oldResizeSaveKey];
650       
651        //remove old view
652        [oldView setHidden: YES];
653        [oldView removeFromSuperview];
654    }
655   
656    //set new tab item
657    NSView * view = [self tabViewForTag: fCurrentTabTag];
658   
659    NSString * resizeSaveKey = nil;
660    NSString * identifier, * title;
661    switch (fCurrentTabTag)
662    {
663        case TAB_INFO_TAG:
664            identifier = TAB_INFO_IDENT;
665            title = NSLocalizedString(@"General Info", "Inspector -> title");
666            break;
667        case TAB_ACTIVITY_TAG:
668            identifier = TAB_ACTIVITY_IDENT;
669            title = NSLocalizedString(@"Activity", "Inspector -> title");
670           
671            [fPiecesView updateView: YES];
672            break;
673        case TAB_TRACKER_TAG:
674            identifier = TAB_TRACKER_IDENT;
675            title = NSLocalizedString(@"Tracker", "Inspector -> title");
676            resizeSaveKey = @"InspectorContentHeightTracker";
677            break;
678        case TAB_PEERS_TAG:
679            identifier = TAB_PEERS_IDENT;
680            title = NSLocalizedString(@"Peers", "Inspector -> title");
681            resizeSaveKey = @"InspectorContentHeightPeers";
682            break;
683        case TAB_FILES_TAG:
684            identifier = TAB_FILES_IDENT;
685            title = NSLocalizedString(@"Files", "Inspector -> title");
686            resizeSaveKey = @"InspectorContentHeightFiles";
687            break;
688        case TAB_OPTIONS_TAG:
689            identifier = TAB_OPTIONS_IDENT;
690            title = NSLocalizedString(@"Options", "Inspector -> title");
691            break;
692        default:
693            return;
694    }
695   
696    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
697   
698    NSWindow * window = [self window];
699   
700    [window setTitle: [NSString stringWithFormat: @"%@ - %@", title, NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]];
701   
702    //selected tab item
703    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
704   
705    NSRect windowRect = [window frame], viewRect = [view frame];
706   
707    if (resizeSaveKey)
708    {
709        float height = [[NSUserDefaults standardUserDefaults] floatForKey: resizeSaveKey];
710        if (height != 0)
711            viewRect.size.height = MAX(height, TAB_MIN_HEIGHT);
712    }
713   
714    float difference = (viewRect.size.height - oldHeight) * [window userSpaceScaleFactor];
715    windowRect.origin.y -= difference;
716    windowRect.size.height += difference;
717   
718    if (resizeSaveKey)
719    {
720        if (!oldResizeSaveKey)
721        {
722            [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height - viewRect.size.height + TAB_MIN_HEIGHT)];
723            [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
724        }
725    }
726    else
727    {
728        [window setMinSize: NSMakeSize([window minSize].width, windowRect.size.height)];
729        [window setMaxSize: NSMakeSize(FLT_MAX, windowRect.size.height)];
730    }
731   
732    viewRect.size.width = windowRect.size.width;
733    [view setFrame: viewRect];
734   
735    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
736    [[window contentView] addSubview: view];
737    [view setHidden: NO];
738   
739    if (fCurrentTabTag == TAB_FILES_TAG)
740        [self updateQuickLook];
741}
742
743- (void) setNextTab
744{
745    int tag = [fTabMatrix selectedTag]+1;
746    if (tag >= [fTabMatrix numberOfColumns])
747        tag = 0;
748   
749    [fTabMatrix selectCellWithTag: tag];
750    [self setTab: nil];
751}
752
753- (void) setPreviousTab
754{
755    int tag = [fTabMatrix selectedTag]-1;
756    if (tag < 0)
757        tag = [fTabMatrix numberOfColumns]-1;
758   
759    [fTabMatrix selectCellWithTag: tag];
760    [self setTab: nil];
761}
762
763- (int) numberOfRowsInTableView: (NSTableView *) tableView
764{
765    if (tableView == fPeerTable)
766        return fPeers ? [fPeers count] : 0;
767    else if (tableView == fTrackerTable)
768        return fTrackers ? [fTrackers count] : 0;
769    return 0;
770}
771
772- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
773{
774    if (tableView == fPeerTable)
775    {
776        NSString * ident = [column identifier];
777        NSDictionary * peer = [fPeers objectAtIndex: row];
778       
779        if ([ident isEqualToString: @"Encryption"])
780            return [[peer objectForKey: @"Encryption"] boolValue] ? [NSImage imageNamed: @"Lock.png"] : nil;
781        else if ([ident isEqualToString: @"Client"])
782            return [peer objectForKey: @"Client"];
783        else if  ([ident isEqualToString: @"Progress"])
784            return [peer objectForKey: @"Progress"];
785        else if ([ident isEqualToString: @"UL To"])
786        {
787            NSNumber * rate;
788            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
789        }
790        else if ([ident isEqualToString: @"DL From"])
791        {
792            NSNumber * rate;
793            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
794        }
795        else
796            return [peer objectForKey: @"IP"];
797    }
798    else if (tableView == fTrackerTable)
799    {
800        id item = [fTrackers objectAtIndex: row];
801        if ([item isKindOfClass: [NSNumber class]])
802            return [NSString stringWithFormat: NSLocalizedString(@"Tier %d", "Inspector -> tracker table"), [item intValue]+1];
803        else
804            return item;
805    }
806    return nil;
807}
808
809- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
810{
811    if (tableView == fPeerTable)
812    {
813        if (fPeers)
814        {
815            NSArray * oldPeers = fPeers;
816            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
817            [oldPeers release];
818            [tableView reloadData];
819        }
820    }
821}
822
823- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) row
824{
825    return NO;
826}
827
828- (BOOL) tableView: (NSTableView *) tableView isGroupRow: (NSInteger) row
829{
830    if (tableView == fTrackerTable)
831        return [[fTrackers objectAtIndex: row] isKindOfClass: [NSNumber class]];
832    return NO;
833}
834
835- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
836                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
837{
838    if (tableView == fPeerTable)
839    {
840        NSDictionary * peer = [fPeers objectAtIndex: row];
841        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 5];
842       
843        [components addObject: [NSString localizedStringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
844            "Inspector -> Peers tab -> table row tooltip"), [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
845       
846        if ([[peer objectForKey: @"Encryption"] boolValue])
847            [components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
848       
849        NSString * portString;
850        int port;
851        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
852            portString = [NSString stringWithFormat: @"%d", port];
853        else
854            portString = NSLocalizedString(@"N/A", "Inspector -> Peers tab -> table row tooltip");
855        [components addObject: [NSString stringWithFormat: @"%@: %@", NSLocalizedString(@"Port",
856            "Inspector -> Peers tab -> table row tooltip"), portString]];
857       
858        switch ([[peer objectForKey: @"From"] intValue])
859        {
860            case TR_PEER_FROM_TRACKER:
861                [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
862                break;
863            case TR_PEER_FROM_INCOMING:
864                [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
865                break;
866            case TR_PEER_FROM_CACHE:
867                [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
868                break;
869            case TR_PEER_FROM_PEX:
870                [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
871                break;
872        }
873       
874        //determing status strings from flags
875        NSMutableArray * statusArray = [NSMutableArray arrayWithCapacity: 3];
876        NSString * flags = [peer objectForKey: @"Flags"];
877       
878        if ([flags rangeOfString: @"D"].location != NSNotFound)
879            [statusArray addObject: NSLocalizedString(@"Currently downloading (interested and not choked)",
880                "Inspector -> peer -> status")];
881        else if ([flags rangeOfString: @"d"].location != NSNotFound)
882            [statusArray addObject: NSLocalizedString(@"You want to download, but peer does not want to send (interested and choked)",
883                "Inspector -> peer -> status")];
884        else;
885       
886        if ([flags rangeOfString: @"U"].location != NSNotFound)
887            [statusArray addObject: NSLocalizedString(@"Currently uploading (interested and not choked)",
888                "Inspector -> peer -> status")];
889        else if ([flags rangeOfString: @"u"].location != NSNotFound)
890            [statusArray addObject: NSLocalizedString(@"Peer wants you to upload, but you do not want to (interested and choked)",
891                "Inspector -> peer -> status")];
892        else;
893       
894        if ([flags rangeOfString: @"K"].location != NSNotFound)
895            [statusArray addObject: NSLocalizedString(@"Peer is unchoking you, but you are not interested",
896                "Inspector -> peer -> status")];
897       
898        if ([flags rangeOfString: @"?"].location != NSNotFound)
899            [statusArray addObject: NSLocalizedString(@"You unchoked the peer, but the peer is not interested",
900                "Inspector -> peer -> status")];
901       
902        if ([statusArray count] > 0)
903            [components addObject: [@"\n" stringByAppendingString: [statusArray componentsJoinedByString: @"\n\n"]]];
904       
905        return [components componentsJoinedByString: @"\n"];
906    }
907    return nil;
908}
909
910// This is the QuickLook delegate method
911// It should return the frame for the item represented by the URL
912// If an empty frame is returned then the panel will fade in/out instead
913- (NSRect) previewPanel: (NSPanel *) panel frameForURL: (NSURL *) url
914{
915    if (fCurrentTabTag == TAB_FILES_TAG && [[fFileController outlineView] numberOfSelectedRows] > 0)
916    {
917        int row = [self visibleRowWithURL: url];
918        if (row != -1)
919        {
920            FileOutlineView * fileOutlineView = [fFileController outlineView];
921           
922            NSRect frame = [fileOutlineView rectOfRow: row];
923            frame.origin = [fileOutlineView convertPoint: frame.origin toView: nil];
924            frame.origin = [[self window] convertBaseToScreen: frame.origin];
925            frame.origin.y -= frame.size.height;
926            return frame;
927        }
928        else
929            return NSZeroRect;
930    }
931    else
932    {
933        NSRect frame = [fImageView frame];
934        frame.origin = [[self window] convertBaseToScreen: frame.origin];
935        return frame;
936    }
937}
938
939#warning needed?
940- (int) visibleRowWithURL: (NSURL *) url
941{
942    FileOutlineView * fileOutlineView = [fFileController outlineView];
943   
944    NSString * fullPath = [url path];
945    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
946    NSRange visibleRows = [fileOutlineView rowsInRect: [fileOutlineView bounds]];
947   
948    int row;
949    for (row = visibleRows.location; row <= row + visibleRows.length; row++)
950    {
951        id rowItem = [fileOutlineView itemAtRow: row];
952        if ([[folder stringByAppendingPathComponent: [rowItem objectForKey: @"Path"]] isEqualToString: fullPath])
953            return row;
954    }
955    return -1;
956}
957
958- (BOOL) quickLookSelectItems
959{
960    if (!fQuickLookAvailable)
961        return NO;
962   
963    NSMutableArray * urlArray = nil;
964   
965    FileOutlineView * fileOutlineView = [fFileController outlineView];
966    if (fCurrentTabTag == TAB_FILES_TAG && [fileOutlineView numberOfSelectedRows] > 0)
967    {
968        Torrent * torrent = [fTorrents objectAtIndex: 0];
969        NSString * folder = [torrent downloadFolder];
970        NSIndexSet * indexes = [fileOutlineView selectedRowIndexes];
971        urlArray = [NSMutableArray arrayWithCapacity: [indexes count]];
972
973        int i;
974        for (i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
975        {
976            NSDictionary * item = [fileOutlineView itemAtRow: i];
977            if ([[item objectForKey: @"IsFolder"] boolValue]
978                || [torrent fileProgress: [[item objectForKey: @"Indexes"] firstIndex]] == 1.0)
979                [urlArray addObject: [NSURL fileURLWithPath: [folder stringByAppendingPathComponent: [item objectForKey: @"Path"]]]];
980        }
981    }
982    else
983    {
984        if ([fTorrents count] > 0)
985        {
986            urlArray = [NSMutableArray arrayWithCapacity: [fTorrents count]];
987            NSEnumerator * enumerator = [fTorrents objectEnumerator];
988            Torrent * torrent;
989            while ((torrent = [enumerator nextObject]))
990            {
991                if ([torrent folder] || [torrent progress] == 1.0)
992                    [urlArray addObject: [NSURL fileURLWithPath: [torrent dataLocation]]];
993            }
994        }
995    }
996   
997    if (urlArray && [urlArray count] > 0)
998    {
999        [[QLPreviewPanel sharedPreviewPanel] setURLs: urlArray currentIndex: 0 preservingDisplayState: YES];
1000        return YES;
1001    }
1002    else
1003        return NO;
1004}
1005
1006- (void) toggleQuickLook: (id) sender
1007{
1008    if (!fQuickLookAvailable)
1009        return;
1010   
1011    if ([[QLPreviewPanel sharedPreviewPanel] isOpen])
1012        [[QLPreviewPanel sharedPreviewPanel] closeWithEffect: 2];
1013    else
1014    {
1015        if ([self quickLookSelectItems])
1016        {
1017            [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFrontWithEffect: 2];
1018            // Restore the focus to our window to demo the selection changing, scrolling (left/right)
1019            // and closing (space) functionality
1020            [[self window] makeKeyWindow];
1021        }
1022    }
1023}
1024
1025- (void) updateQuickLook
1026{
1027    if (!fQuickLookAvailable || ![[QLPreviewPanel sharedPreviewPanel] isOpen])
1028        return;
1029   
1030    //if the user changes the selection while the panel is open then update the current items
1031    if (![self quickLookSelectItems])
1032        [[QLPreviewPanel sharedPreviewPanel] closeWithEffect: 1];
1033}
1034
1035- (void) quickLookPressLeft
1036{
1037    if (fQuickLookAvailable && [[QLPreviewPanel sharedPreviewPanel] isOpen])
1038        [[QLPreviewPanel sharedPreviewPanel] selectPreviousItem];
1039}
1040
1041- (void) quickLookPressRight
1042{
1043    if (fQuickLookAvailable && [[QLPreviewPanel sharedPreviewPanel] isOpen])
1044        [[QLPreviewPanel sharedPreviewPanel] selectNextItem];
1045}
1046
1047- (void) keyDown: (NSEvent *) event
1048{
1049    unichar firstChar = [[event charactersIgnoringModifiers] characterAtIndex: 0];
1050    if (firstChar == ' ')
1051        [self toggleQuickLook: nil];
1052    else if (firstChar == NSRightArrowFunctionKey)
1053        [self quickLookPressRight];
1054    else if (firstChar == NSLeftArrowFunctionKey)
1055        [self quickLookPressLeft];
1056    else
1057        [super keyDown: event]; 
1058}
1059
1060- (void) setPiecesView: (id) sender
1061{
1062    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
1063}
1064
1065- (void) setPiecesViewForAvailable: (BOOL) available
1066{
1067    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
1068    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
1069   
1070    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
1071    [fPiecesView updateView: YES];
1072}
1073
1074- (void) revealTorrentFile: (id) sender
1075{
1076    if ([fTorrents count] > 0)
1077        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
1078}
1079
1080- (void) revealDataFile: (id) sender
1081{
1082    if ([fTorrents count] > 0)
1083        [[fTorrents objectAtIndex: 0] revealData];
1084}
1085
1086- (void) setSpeedMode: (id) sender
1087{
1088    BOOL upload = sender == fUploadLimitPopUp;
1089    int mode;
1090    switch ([sender indexOfSelectedItem])
1091    {
1092        case OPTION_POPUP_LIMIT:
1093            mode = TR_SPEEDLIMIT_SINGLE;
1094            break;
1095        case OPTION_POPUP_NO_LIMIT:
1096            mode = TR_SPEEDLIMIT_UNLIMITED;
1097            break;
1098        case OPTION_POPUP_GLOBAL:
1099            mode = TR_SPEEDLIMIT_GLOBAL;
1100            break;
1101        default:
1102            return;
1103    }
1104   
1105    Torrent * torrent;
1106    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1107    while ((torrent = [enumerator nextObject]))
1108        [torrent setSpeedMode: mode upload: upload];
1109   
1110    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
1111   
1112    BOOL single = mode == TR_SPEEDLIMIT_SINGLE;
1113    [field setHidden: !single];
1114    if (single)
1115    {
1116        [field selectText: self];
1117        [[self window] makeKeyAndOrderFront:self];
1118    }
1119   
1120    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
1121    [label setHidden: !single];
1122}
1123
1124- (void) setSpeedLimit: (id) sender
1125{
1126    BOOL upload = sender == fUploadLimitField;
1127    int limit = [sender intValue];
1128   
1129    Torrent * torrent;
1130    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1131   
1132    while ((torrent = [enumerator nextObject]))
1133        [torrent setSpeedLimit: limit upload: upload];
1134}
1135
1136- (void) setRatioSetting: (id) sender
1137{
1138    int setting;
1139    switch ([sender indexOfSelectedItem])
1140    {
1141        case OPTION_POPUP_LIMIT:
1142            setting = NSOnState;
1143            break;
1144        case OPTION_POPUP_NO_LIMIT:
1145            setting = NSOffState;
1146            break;
1147        case OPTION_POPUP_GLOBAL:
1148            setting = NSMixedState;
1149            break;
1150        default:
1151            return;
1152    }
1153   
1154    Torrent * torrent;
1155    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1156    while ((torrent = [enumerator nextObject]))
1157        [torrent setRatioSetting: setting];
1158   
1159    BOOL single = setting == NSOnState;
1160    [fRatioLimitField setHidden: !single];
1161    if (single)
1162    {
1163        [fRatioLimitField selectText: self];
1164        [[self window] makeKeyAndOrderFront:self];
1165    }
1166   
1167    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1168}
1169
1170- (void) setRatioLimit: (id) sender
1171{
1172    float limit = [sender floatValue];
1173   
1174    Torrent * torrent;
1175    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1176    while ((torrent = [enumerator nextObject]))
1177        [torrent setRatioLimit: limit];
1178}
1179
1180- (void) setPeersConnectLimit: (id) sender
1181{
1182    int limit = [sender intValue];
1183   
1184    Torrent * torrent;
1185    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1186    while ((torrent = [enumerator nextObject]))
1187        [torrent setMaxPeerConnect: limit];
1188}
1189
1190
1191- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
1192{
1193    [fInitialString release];
1194    fInitialString = [[control stringValue] retain];
1195   
1196    return YES;
1197}
1198
1199- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
1200{
1201    NSBeep();
1202    if (fInitialString)
1203    {
1204        [control setStringValue: fInitialString];
1205        [fInitialString release];
1206        fInitialString = nil;
1207    }
1208    return NO;
1209}
1210
1211@end
1212
1213@implementation InfoWindowController (Private)
1214
1215- (void) updateInfoGeneral
1216{   
1217    if ([fTorrents count] != 1)
1218        return;
1219   
1220    Torrent * torrent = [fTorrents objectAtIndex: 0];
1221   
1222    [fTrackerField setStringValue: [torrent trackerAddressAnnounce]];
1223   
1224    NSString * location = [torrent dataLocation];
1225    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
1226    [fDataLocationField setToolTip: location];
1227}
1228
1229- (void) updateInfoActivity
1230{
1231    int numberSelected = [fTorrents count];
1232    if (numberSelected == 0)
1233        return;
1234   
1235    uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
1236    NSDate * lastActivity = nil;
1237    Torrent * torrent;
1238    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1239    while ((torrent = [enumerator nextObject]))
1240    {
1241        have += [torrent haveTotal];
1242        haveVerified += [torrent haveVerified];
1243        downloadedTotal += [torrent downloadedTotal];
1244        uploadedTotal += [torrent uploadedTotal];
1245        failedHash += [torrent failedHash];
1246       
1247        NSDate * nextLastActivity;
1248        if ((nextLastActivity = [torrent dateActivity]))
1249            lastActivity = lastActivity ? [lastActivity laterDate: nextLastActivity] : nextLastActivity;
1250    }
1251   
1252    if (have == 0)
1253        [fHaveField setStringValue: [NSString stringForFileSize: 0]];
1254    else
1255    {
1256        NSString * verifiedString = [NSString stringWithFormat: NSLocalizedString(@"%@ verified", "Inspector -> Activity tab -> have"),
1257                                        [NSString stringForFileSize: haveVerified]];
1258        if (have == haveVerified)
1259            [fHaveField setStringValue: verifiedString];
1260        else
1261            [fHaveField setStringValue: [NSString stringWithFormat: @"%@ (%@)", [NSString stringForFileSize: have], verifiedString]];
1262    }
1263   
1264    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
1265    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
1266    [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
1267   
1268    [fDateActivityField setObjectValue: lastActivity];
1269   
1270    if (numberSelected == 1)
1271    {
1272        torrent = [fTorrents objectAtIndex: 0];
1273       
1274        [fStateField setStringValue: [torrent stateString]];
1275       
1276        if ([torrent folder])
1277            [fProgressField setStringValue: [NSString localizedStringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
1278                "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
1279        else
1280            [fProgressField setStringValue: [NSString localizedStringWithFormat: @"%.2f%%", 100.0 * [torrent progress]]];
1281           
1282        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
1283        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
1284       
1285        NSString * errorMessage = [torrent errorMessage];
1286        if (![errorMessage isEqualToString: [fErrorMessageView string]])
1287        {
1288            [fErrorMessageView setString: errorMessage];
1289            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
1290        }
1291       
1292        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
1293       
1294        [fPiecesView updateView: NO];
1295    }
1296}
1297
1298#warning reload table when necessary?
1299- (void) updateInfoTracker
1300{
1301    if ([fTorrents count] != 1)
1302        return;
1303    Torrent * torrent = [fTorrents objectAtIndex: 0];
1304   
1305    //announce fields
1306    NSString * announceAddress = [torrent trackerAddressAnnounce];
1307    [fAnnounceAddressField setStringValue: announceAddress];
1308    [fAnnounceAddressField setToolTip: announceAddress];
1309   
1310    [fAnnounceLastField setObjectValue: [torrent lastAnnounceTime]];
1311   
1312    NSString * announceResponse = [torrent announceResponse];
1313    [fAnnounceResponseField setStringValue: announceResponse];
1314    [fAnnounceResponseField setToolTip: announceResponse];
1315    [fAnnounceResponseField setSelectable: ![announceResponse isEqualToString: @""]];
1316   
1317    int announceNext = [torrent nextAnnounceTime];
1318    [fAnnounceNextField setStringValue: announceNext > 0 ? [NSString timeString: announceNext showSeconds: YES] : @""];
1319   
1320    //scrape fields
1321    NSString * scrapeAddress;
1322    if ((scrapeAddress = [torrent trackerAddressScrape]))
1323    {
1324        [fScrapeAddressField setStringValue: scrapeAddress];
1325        [fScrapeAddressField setToolTip: scrapeAddress];
1326    }
1327    else
1328    {
1329        [fScrapeAddressField setStringValue: @""];
1330        [fScrapeAddressField setToolTip: @""];
1331    }
1332   
1333    [fScrapeLastField setObjectValue: [torrent lastScrapeTime]];
1334   
1335    NSString * scrapeResponse = [torrent scrapeResponse];
1336    [fScrapeResponseField setStringValue: scrapeResponse];
1337    [fScrapeResponseField setToolTip: scrapeResponse];
1338    [fScrapeResponseField setSelectable: ![scrapeResponse isEqualToString: @""]];
1339   
1340    int scrapeNext = [torrent nextScrapeTime];
1341    [fScrapeNextField setStringValue: scrapeNext > 0 ? [NSString timeString: scrapeNext showSeconds: YES] : @""];
1342}
1343
1344- (void) updateInfoPeers
1345{
1346    if ([fTorrents count] != 1)
1347        return;
1348    Torrent * torrent = [fTorrents objectAtIndex: 0];
1349   
1350    int seeders = [torrent seeders], leechers = [torrent leechers], completed = [torrent completedFromTracker];
1351    [fSeedersField setStringValue: seeders >= 0 ? [NSString stringWithFormat: @"%d", seeders] : @""];
1352    [fLeechersField setStringValue: leechers >= 0 ? [NSString stringWithFormat: @"%d", leechers] : @""];
1353    [fCompletedFromTrackerField setStringValue: completed >= 0 ? [NSString stringWithFormat: @"%d", completed] : @""];
1354   
1355    BOOL active = [torrent isActive];
1356   
1357    if (active)
1358    {
1359        int total = [torrent totalPeersConnected];
1360        NSString * connected = [NSString stringWithFormat:
1361                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
1362       
1363        if (total > 0)
1364        {
1365            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
1366            int count;
1367            if ((count = [torrent totalPeersTracker]) > 0)
1368                [components addObject: [NSString stringWithFormat:
1369                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
1370            if ((count = [torrent totalPeersIncoming]) > 0)
1371                [components addObject: [NSString stringWithFormat:
1372                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
1373            if ((count = [torrent totalPeersPex]) > 0)
1374                [components addObject: [NSString stringWithFormat:
1375                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
1376            if ((count = [torrent totalPeersCache]) > 0)
1377                [components addObject: [NSString stringWithFormat:
1378                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
1379           
1380            connected = [connected stringByAppendingFormat: @": %@", [components componentsJoinedByString: @", "]];
1381        }
1382       
1383        [fConnectedPeersField setStringValue: connected];
1384       
1385        [fDownloadingFromField setIntValue: [torrent peersSendingToUs]];
1386        [fUploadingToField setIntValue: [torrent peersGettingFromUs]];
1387    }
1388    else
1389    {
1390        [fConnectedPeersField setStringValue: @""];
1391        [fDownloadingFromField setStringValue: @""];
1392        [fUploadingToField setStringValue: @""];
1393    }
1394   
1395    [fKnownField setIntValue: [torrent totalPeersKnown]];
1396   
1397    [fPeers release];
1398    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
1399   
1400    [fPeerTable reloadData];
1401}
1402
1403- (void) updateInfoFiles
1404{
1405    if ([fTorrents count] == 1)
1406        [fFileController reloadData];
1407}
1408
1409- (NSView *) tabViewForTag: (int) tag
1410{
1411    switch (tag)
1412    {
1413        case TAB_INFO_TAG:
1414            return fInfoView;
1415        case TAB_ACTIVITY_TAG:
1416            return fActivityView;
1417        case TAB_TRACKER_TAG:
1418            return fTrackerView;
1419        case TAB_PEERS_TAG:
1420            return fPeersView;
1421        case TAB_FILES_TAG:
1422            return fFilesView;
1423        case TAB_OPTIONS_TAG:
1424            return fOptionsView;
1425        default:
1426            return nil;
1427    }
1428}
1429
1430- (NSArray *) peerSortDescriptors
1431{
1432    NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
1433   
1434    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
1435    BOOL useSecond = YES, asc = YES;
1436    if ([oldDescriptors count] > 0)
1437    {
1438        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
1439        [descriptors addObject: descriptor];
1440       
1441        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
1442            asc = [descriptor ascending];
1443    }
1444   
1445    //sort by IP after primary sort
1446    if (useSecond)
1447    {
1448        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
1449                                                                        selector: @selector(compareIP:)];
1450        [descriptors addObject: secondDescriptor];
1451        [secondDescriptor release];
1452    }
1453   
1454    return descriptors;
1455}
1456
1457@end
Note: See TracBrowser for help on using the repository browser.