source: trunk/macosx/InfoWindowController.m @ 3611

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

change tiff's to png's

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